【问题标题】:IStorage does not unlock after commit提交后 IStorage 未解锁
【发布时间】:2022-10-17 16:28:07
【问题描述】:

当我运行下面的 prog 时,stgOpenStorage 的结果值为 STG_E_SHAREVIOLATION。我应该如何关闭 IStorage 才能解锁?

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;

以及如何设置 IStorage/IStream 默认大小/大小步长?因为我测试的 1.6K 字节内容存储在 16K 中。

【问题讨论】:

    标签: delphi ole istorage


    【解决方案1】:

    Delphi 源库中有两个 IStorage 实现。 WinApi.OLE2WinApi.ActiveX。你用哪一个?在WinApi.OLE2 单元中,IStorage 和 IStream 是类,它们不是接口。如果您使用本机,则界面乱码不起作用。如果你使用WinApi.ActiveX 单位,这个例子就可以正常工作。

    【讨论】:

    • 确切地!情况就是这样。我使用了 OLE2 单元...对于 ActiveX 单元,该示例也适用于我。谢谢。
    【解决方案2】:

    代码看起来不错1,所以我感觉问题是由于您使用了STGM_SHARE_EXCLUSIVE。该文件在您尝试打开它时正在使用,所以我打赌您的 OS/AV 是保持文件打开(即扫描其内容)的那个,而不是storeTextIntoStorageStream() 中的接口,它们是输入readTextFromStorageStream() 时早已不复存在。

    1:好吧,除了缺乏足够的错误处理。以及接口变量的冗余nil'ing。并且,请考虑用 PChar() 强制转换替换您的字符串索引。

    readTextFromStorageStream() 中,尝试用STGM_SHARE_DENY_WRITE 替换STGM_SHARE_EXCLUSIVE(这对作者有意义,但对读者没有意义),看看错误是否消失:

    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:	emp	est.stg';
      streamName := 'Stream-0';
      storeTextIntoStorageStream( Memo1.Lines.DelimitedText );
      Memo1.Lines.DelimitedText := readTextFromStorageStream;
    end;
    

    【讨论】:

    • 如果我调用手动发布,则代码可以正常工作(回读书面内容)。所以 Ref 计数是问题所在。这不是时间问题。因为文件一直处于锁定状态,直到我退出编。 (如果我再次按下保存按钮,保存操作会出现相同的错误消息)在下一次运行时,它可以删除并重新创建文件。保存速度很快。在我的测试用例中,数据只有 1.6K。您的代码以相同的方式运行。
    • "所以 Ref 计数是问题所在" - 如果这是真的,那么 Stg API 将存在严重缺陷。您的代码只有 1 个对 IStorage 的引用和 1 个对 IStream 的引用。这些引用被清除自动地当它们超出范围时由 Delphi 编译器,以及当您将 nil 分配给它们时。但是直接调用Release()会导致双倍的- 释放接口,这违反了 COM 引用计数规则,是一个很大的禁忌。上网看看,StgCreateDocFile()/StgOpenStorage() 的例子都不会调用IStorage.Release() 两次,或者IStream.Release() 两次,没有额外的AddRef()s。
    • 我很清楚这一点,但它解决了这种情况下的问题。
    • 我看不出它如何解决任何问题。你的提议是对 COM 的公然违反,它不能按照您描述的方式工作。无论如何,我尝试了您最初提供的代码以及我提供的代码,它们对我来说都工作得很好,没有错误,文件在两种情况下都按预期正确创建和读取。因此,无论您的设置发生了什么,都必须是您的特定环境的问题。您只需要自己调试它。
    • 有一些关于同样问题的谈判。但它在 C# 解决方案中,我什至一个字都听不懂。这是关于元帅和统计记录...stackoverflow.com/questions/1661905/…
    猜你喜欢
    • 2021-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-29
    • 1970-01-01
    • 2015-09-12
    • 2022-01-12
    相关资源
    最近更新 更多