procedure CheckHResult(Code: HRESULT); begin if not Succeeded(Code) then RaiseLastOSError; end; var TestFilePath: string; WideBuff: WideString; Root: IStorage; begin TestFilePath := ExtractFilePath(ParamStr(0)) + '..\data\simple.bin'; ForceDirectories(ExtractFilePath(TestFilePath)); WideBuff := TestFilePath; CheckHResult(StgCreateDocfile(@WideBuff[1], STGM_CREATE or STGM_WRITE or STGM_SHARE_EXCLUSIVE, 0, Root));
procedure WriteFile(Storage: IStorage; AName: WideString; Data: AnsiString); var Stream: IStream; OS: TOleStream; begin CheckHResult(Storage.CreateStream(@AName[1], STGM_WRITE or STGM_SHARE_EXCLUSIVE, 0, 0, Stream)); OS := TOleStream.Create(Stream); try OS.WriteBuffer(Data[1], Length(Data)); finally OS.Free; end; end;
WriteFile(Root, 'RootFile', 'First file data');
CheckHResult(Root.CreateStorage('SubFolder', STGM_WRITE or STGM_SHARE_EXCLUSIVE, 0, 0, Folder));
WriteFile(Folder, 'SubFolderFile', 'Second file data');
CheckHResult(Root.Commit(STGC_DEFAULT));
CheckHResult(Root.Revert);
var TestFilePath: string; WideBuff: WideString; Root: IStorage; begin TestFilePath := ExtractFilePath(ParamStr(0)) + '..\data\simple.bin'; WideBuff := TestFilePath; CheckHResult(StgOpenStorage(@WideBuff[1], nil, STGM_READ or STGM_SHARE_DENY_WRITE, nil, 0, Root));
var Enum: IEnumStatStg; begin CheckHResult(Storage.EnumElements(0, nil, 0, Enum));
if (CoGetMalloc(1, ShellMalloc) <> S_OK) or (ShellMalloc = nil) then raise Exception.Create('CoGetMalloc failed.');
ShellMalloc.Free(TmpElement.pwcsName);
procedure Enumerate(const Root: string; Storage: IStorage); var Enum: IEnumStatStg; TmpElement: TStatStg; ShellMalloc: IMalloc; Fetched: Int64; Folder: IStorage; AFile: IStream; begin // .. OLE, IMalloc if (CoGetMalloc(1, ShellMalloc) <> S_OK) or (ShellMalloc = nil) then raise Exception.Create('CoGetMalloc failed.'); // CheckHResult(Storage.EnumElements(0, nil, 0, Enum)); // Fetched := 1; while Fetched > 0 do if Enum.Next(1, TmpElement, @Fetched) = S_OK then // ( ) if ShellMalloc.DidAlloc(TmpElement.pwcsName) = 1 then begin // Write('Found: ', Root, '\', AnsiString(TmpElement.pwcsName)); // case TmpElement.dwType of // - STGTY_STREAM: begin Writeln(' - file: ', sLineBreak); CheckHResult(Storage.OpenStream(TmpElement.pwcsName, nil, STGM_READ or STGM_SHARE_EXCLUSIVE, 0, AFile)); ShowFileData(AFile); Writeln; end; // - STGTY_STORAGE: begin Writeln(' - folder'); CheckHResult(Storage.OpenStorage(TmpElement.pwcsName, nil, STGM_READ or STGM_SHARE_EXCLUSIVE, nil, 0, Folder)); Enumerate(Root + '\' + string(TmpElement.pwcsName), Folder); end; else Writeln('Unsupported type: ', TmpElement.dwType); end; // , - ShellMalloc.Free(TmpElement.pwcsName); end; end;
[dcc32 Warning] FWStorage.pas (860): W1057 Implicit string cast from 'AnsiString' to 'string'
[dcc32 Warning] uStgReader.pas (102): W1057 Implicit string cast from 'ShortString' to 'string'
procedure TForm1.Button1Click(Sender: TObject); var Path: string; Storage: TFWStorage; Root, Folder: TFWStorageCursor; Data: TStringStream; begin Storage := TFWStorage.Create; try // Path := ExpandFileName(ExtractFilePath(ParamStr(0)) + '..\data\test.bin'); // Storage.OpenFile(Path, True, Root); // Storage.ForceStorage(Path + '\Subfolder1\subloder2\subsubfolder', Folder); Data := TStringStream.Create; try // Data.WriteString('new file data.'); // while Folder <> Root do begin Folder.WriteStream(Folder.GetName + '_new_file.txt', Data); // Folder.Backward(Folder); end; // Root.FlushBuffer; finally Data.Free; end; finally Storage.Free; end; end;
private FCurrentFileName: string; FStorage: TFWStorage; FRoot: TFWStorageCursor;
procedure TForm1.FormCreate(Sender: TObject); begin // FCurrentFileName := ExpandFileName(ExtractFilePath(ParamStr(0)) + '..\data\simple.bin'); // FStorage := TFWStorage.Create; // OpenFile(False); end;
procedure TForm1.OpenFile(CreateNew: Boolean); begin // , FStorage.CloseFile; // FStorage.OpenFile(FCurrentFileName, CreateNew or not FileExists(FCurrentFileName), FRoot); Caption := FCurrentFileName; // ShowStorageData(FRoot); end;
procedure TForm1.ShowStorageData(AStorage: TFWStorageCursor); procedure AddItem(const ACaption: string; AIndex: Integer); begin with ListView1.Items.Add do begin Caption := ACaption; case AIndex of -1: ImageIndex := -1; 1: begin ImageIndex := 0; SubItems.Add('Folder'); end else ImageIndex := 1; SubItems.Add('File'); end; // Data, : // -1 - // 0 - // 1 - // Data := Pointer(AIndex); end; end; var AData: TFWStorageEnum; I: Integer; begin ListView1.Items.BeginUpdate; try ListView1.Items.Clear; // , // ( - ) if not AStorage.IsRoot then AddItem('..', -1); // AStorage.Enumerate(AData); // ListView for I := 0 to AData.Count - 1 do AddItem( string(AData.ElementEnum[I].pacsName), Byte(AData.ElementEnum[I].dwType = STGTY_STORAGE)); finally ListView1.Items.EndUpdate; end; end;
procedure TForm1.ListView1DblClick(Sender: TObject); begin // - if ListView1.Selected = nil then Exit; // Data case Integer(ListView1.Selected.Data) of -1: // begin // FRoot.Backward(FRoot); // ShowStorageData(FRoot); end; 0: // EditFile; 1: // begin // FRoot.OpenStorage(AnsiString(ListView1.Selected.Caption), FRoot); // ShowStorageData(FRoot); end; end; end;
procedure TForm1.EditFile; var Buff: TMemoryStream; Data: AnsiString; begin Buff := TMemoryStream.Create; try // FRoot.ReadStream(AnsiString(ListView1.Selected.Caption), Buff); // if Buff.Size > 0 then begin SetLength(Data, Buff.Size); Buff.Read(Data[1], Buff.Size); end; // frmEdit := TfrmEdit.Create(Self); try // Memo frmEdit.Memo1.Text := string(Data); // if frmEdit.ShowModal <> mrOk then Exit; // Memo Buff.Clear; Data := AnsiString(frmEdit.Memo1.Text); if Length(Data) > 0 then Buff.Write(Data[1], Length(Data)); // FRoot.WriteStream(AnsiString(ListView1.Selected.Caption), Buff); // FRoot.FlushBuffer; finally frmEdit.Release; end; finally Buff.Free; end; end;
procedure TForm1.btnCreateDFaseClick(Sender: TObject); begin if SaveDialog1.Execute then begin FCurrentFileName := SaveDialog1.FileName; OpenFile(True); end; end; procedure TForm1.btnOpenDBaseClick(Sender: TObject); begin if OpenDialog1.Execute then begin FCurrentFileName := OpenDialog1.FileName; OpenFile(False); end; end;
procedure TForm1.btnAddFolderClick(Sender: TObject); var NewFolderName: string; Tmp: TFWStorageCursor; begin if InputQuery('New folder', 'Enter folder name', NewFolderName) then begin FRoot.CreateStorage(AnsiString(NewFolderName), Tmp); FRoot.FlushBuffer; end; ShowStorageData(FRoot); end; procedure TForm1.btnDelFolderClick(Sender: TObject); begin if Application.MessageBox( PChar(Format('Delete folder: "%s"?', [ListView1.Selected.Caption])), 'Delete folder', MB_ICONQUESTION or MB_YESNO) = ID_YES then begin FRoot.DeleteStorage(AnsiString(ListView1.Selected.Caption)); FRoot.FlushBuffer; ShowStorageData(FRoot); end; end;
procedure TForm1.btnAddFileClick(Sender: TObject); var NewFileName: string; begin if InputQuery('New file', 'Enter file name', NewFileName) then begin FRoot.CreateStream(AnsiString(NewFileName)); FRoot.FlushBuffer; end; ShowStorageData(FRoot); end; procedure TForm1.btnDelFileClick(Sender: TObject); begin if Application.MessageBox( PChar(Format('Delete file: "%s"?', [ListView1.Selected.Caption])), 'Delete file', MB_ICONQUESTION or MB_YESNO) = ID_YES then begin FRoot.DeleteStream(AnsiString(ListView1.Selected.Caption)); FRoot.FlushBuffer; ShowStorageData(FRoot); end; end;
TPoifsFileHeader = packed record // . (0 x E011CFD0, 0 x E11AB1A1) _abSig: array [0..7] of Byte; // Class ID. WriteClassStg, GetClassFile/ReadClassStg. // Excel = 0 _clid: TGUID; // . _uMinorVersion: USHORT; // Dll/ _uDllVersion: USHORT; // 0 x FFFE , Intel _uByteOrder: USHORT; // . 9, 512 (2 ^ 9) _uSectorShift: USHORT; // -. 6, 64 (2 ^ 6) _uMiniSectorShift: USHORT; // , 0 _usReserved: USHORT; // , 0 _ulReserved1: ULONG; // , 0 _ulReserved2: ULONG; // , FAT. // 7, 1, , 1 DIF . _csectFat: ULONG; // , Property Set Storage // ( FAT Directory Root Directory Entry) _sectDirStart: ULONG; // . _signature: ULONG; // -. 4096 _ulMiniSectorCutoff: ULONG; // -FAT. // (-2), - . _sectMiniFatStart: ULONG; // -FAT. 0, - _csectMiniFat: ULONG; // DIF . // 7, DIF (-2) _sectDifStart: ULONG; // DIF .0, < 7 _csectDif: ULONG; // 109 , FAT. // 7, , (-1). _sectFat: array [0..108] of ULONG; end;
procedure TPoifsFile.InitHeader; begin FStream.ReadBuffer(FHeader, SizeOf(TPoifsFileHeader)); FHeader._uSectorShift := Round(IntPower(2, FHeader._uSectorShift)); FHeader._uMiniSectorShift := Round(IntPower(2, FHeader._uMiniSectorShift)); end;
procedure TPoifsFile.ComposeFAT; var I, J, X, FatLength: Integer; FatBlock: TPoifsFatBlock; CurrentFat, Offset: Integer; XFat: array of Integer; begin // - FAT ( 128 ) // DIF , _csectFat FatLength := FHeader._csectFat * 128; // FAT SetLength(FFat, FatLength); // SetLength(FFatOffset, FatLength); // DIF , FAT 109 // DIF for I := 0 to IfThen(FHeader._csectDif > 0, 108, FHeader._csectFat - 1) do begin // FAT 128 FatBlock := TPoifsFatBlock(GetBlock(FHeader._sectFat[I])); for J := 0 to 127 do begin FFat[I * 128 + J] := FatBlock[J]; // , FFatOffset[I * 128 + J] := FStream.Position - SizeOf(TPoifsBlock); end; end; // , DIF if FHeader._sectDifStart = 0 then Exit; // , FAT Offset := FHeader._sectDifStart; // XFAT FAT SetLength(XFat, 128); // FAT CurrentFat := 13951; //109 * 128 - 1 BAT for X := 0 to FHeader._csectDif - 1 do begin // ( _uSectorShift ) // FStream.Position := GetBlockOffset(Offset); // FAT FStream.ReadBuffer(XFat[0], 128 * SizeOf(DWORD)); // // // for I := 0 to 126 do begin // , , // FAT if XFat[I] < 0 then Exit; // FAT 128 FatBlock := TPoifsFatBlock(GetBlock(XFat[I])); for J := 0 to 127 do begin Inc(CurrentFat); FFat[CurrentFat] := FatBlock[J]; FFatOffset[CurrentFat] := FStream.Position - SizeOf(TPoifsBlock); end; end; // Offset := XFat[127]; end; end;
function TPoifsFile.GetBlockOffset(BlockIndex: Integer): Int64; begin Result := HEADER_SIZE + FHeader._uSectorShift * BlockIndex; end; function TPoifsFile.GetBlock(Adress: Integer): TPoifsBlock; begin FStream.Position := GetBlockOffset(Adress); FStream.ReadBuffer(Result, SizeOf(TPoifsBlock)); end;
procedure TPoifsFile.ComposeMiniFat; var I, CurrChain: Integer; TmpPosition: int64; begin // CurrChain := FHeader._sectMiniFatStart; // (- 128 ) SetLength(FMiniFat, FHeader._csectMiniFat * 128); I := 0; while Integer(CurrChain) >= 0 do begin // TmpPosition := GetBlockOffset(CurrChain); // , if TmpPosition < 0 then Exit; //if TmpPosition > FStream.Size then Exit; FStream.Position := TmpPosition; // FStream.ReadBuffer(FMiniFat[I], 512 {128 * SizeOf(DWORD)}); Inc(I, 128); // FAT CurrChain := FFat[CurrChain]; end; end;
TPoifsProperty = packed record // 128 length // / Caption: array[0..31] of WChar; // CaptionSize: Word; // STGTY_ PropertyType: Byte; // ( TPoifsProperty Red-Black-Tree) NodeColor: Byte; // 0 (red) or 1 (black) // PreviousProp: Integer; // NextProp: Integer; // ChildProp: Integer; Reserved1: TGUID; UserFlags: DWORD; // ATime: array [0..1] of Int64; // FAT StartBlock: Integer; // Size: Integer; Reserved2: DWORD; end; TPoifsPropsBlock = array[0..3] of TPoifsProperty;
function TPoifsFile.ReadPropsArray: Boolean; var I, J, Len: Integer; PropsBlock: TPoifsPropsBlock; begin Result := True; // Len := 0; // , Property Set Storage J := FHeader._sectDirStart; repeat // 4 Inc(Len, 4); SetLength(FPropsArray, Len); PropsBlock := TPoifsPropsBlock(GetBlock(J)); for I := 0 to 3 do FPropsArray[Len - 4 + I] := PropsBlock[I]; // FAT J := FFat[J]; until J = ENDOFCHAIN; end;
begin FileStream := TFileStream.Create(edSrc.Text, fmOpenReadWrite); try AFile := TPoifsFile.Create(FileStream); try // AFile.LoadFromStream; ATree := TStorageTree.Create; try // for I := 0 to AFile.PropertiesCount - 1 do ATree.AddNode(I).Data := AFile[I]; // FillAllChilds(0, ATree.GetNode(0).Data.ChildProp); // TreeView1.Items.Clear; FillTree(nil, 0); // DebugLog := TStringList.Create; try Extract(IncludeTrailingPathDelimiter(edDst.Text), 0); if DebugLog.Count > 0 then DebugLog.SaveToFile(IncludeTrailingPathDelimiter(edDst.Text) + 'cannotread.log'); finally DebugLog.Free; end; finally ATree.Free; end; finally AFile.Free; end; finally FileStream.Free; end; end;
var ATree: TStorageTree; ... procedure FillAllChilds(RootIndex, CurrentIndex: Integer); var SubChildIndex: Integer; RootNode, CurrNode, ChildNode: TStorageElement; begin if CurrentIndex < 0 then Exit; // RootNode := ATree.GetNode(RootIndex); // CurrNode := ATree.GetNode(CurrentIndex); if CurrNode = nil then Exit; // - if CurrNode.Added then Exit; CurrNode.Added := True; // ATree.AddVector(RootNode, CurrNode); // FillAllChilds(CurrNode.ID, CurrNode.Data.ChildProp); // SubChildIndex := CurrNode.Data.PreviousProp; while SubChildIndex >= 0 do begin // , FillAllChilds(RootIndex, SubChildIndex); ChildNode := ATree.GetNode(SubChildIndex); if ChildNode <> nil then SubChildIndex := ChildNode.Data.PreviousProp else SubChildIndex := -1; end; // , SubChildIndex := CurrNode.Data.NextProp; while SubChildIndex >= 0 do begin FillAllChilds(RootIndex, SubChildIndex); ChildNode := ATree.GetNode(SubChildIndex); if ChildNode <> nil then SubChildIndex := ChildNode.Data.NextProp else SubChildIndex := -1; end; end;
procedure FillTree(Node: TTreeNode; RootNodeIndex: Integer); var W: WideString; TreeNode: TTreeNode; I: Integer; RootStorageNode, ChildStorageNode: TStorageElement; begin // RootStorageNode := ATree.GetNode(RootNodeIndex); // ( ) W := RootStorageNode.Data.Caption; TreeNode := TreeView1.Items.AddChildFirst(Node, W); case RootStorageNode.Data.PropertyType of STGTY_STORAGE: TreeNode.ImageIndex := 0; STGTY_STREAM: TreeNode.ImageIndex := 1; end; // for I := 0 to RootStorageNode.VectorCount - 1 do begin // , ( ?) ChildStorageNode := TStorageElement(RootStorageNode.GetVector(I).SlaveNode); if ChildStorageNode = nil then Continue; // , , if ChildStorageNode.ID <> RootNodeIndex then FillTree(TreeNode, ChildStorageNode.ID); end; end;
procedure TPoifsFile.GetDataFromStream(ChainStart: ULONG; NeedLength: DWORD; const Stream: TStream); begin Stream.Size := 0; while (Integer(ChainStart) >= 0) and (Stream.Size < NeedLength) do begin // FStream.Position := GetBlockOffset(ChainStart); // ChainStart := FFat[ChainStart]; // Stream.CopyFrom(FStream, FHeader._uSectorShift); end; // if Stream.Size > NeedLength then Stream.Size := NeedLength; end;
procedure TPoifsFile.GetDataFromMiniStream(ChainStart: ULONG; NeedLength: DWORD; const Stream: TStream); var MiniStreamOffset: DWORD; RealMiniStreamSector, TmpChain: Integer; begin Stream.Size := 0; while (Integer(ChainStart) >= 0) and (Stream.Size < NeedLength) do begin // Ministream TmpChain := ChainStart; RealMiniStreamSector := Properties[0].StartBlock; while TmpChain >= 8 do begin Dec(TmpChain, 8); RealMiniStreamSector := FFat[RealMiniStreamSector]; end; // MiniStreamOffset := GetBlockOffset(RealMiniStreamSector); // FStream.Position := MiniStreamOffset + (ChainStart mod 8) * FHeader._uMiniSectorShift; // ChainStart := FMiniFat[ChainStart]; // Stream.CopyFrom(FStream, FHeader._uMiniSectorShift); end; // if Stream.Size > NeedLength then Stream.Size := NeedLength; end;
procedure GetStorageData(ANode: TStorageElement; const Stream: TStream); begin if ANode.Data.Size < Integer(AFile.Header._ulMiniSectorCutoff) then AFile.GetDataFromMiniStream(ANode.Data.StartBlock, ANode.Data.Size, Stream) else AFile.GetDataFromStream(ANode.Data.StartBlock, ANode.Data.Size, Stream); end;
procedure Extract(Path: string; RootNodeIndex: Integer); var W: WideString; I: Integer; RootStorageNode, ChildStorageNode: TStorageElement; F: TFileStream; begin RootStorageNode := ATree.GetNode(RootNodeIndex); W := RootStorageNode.Data.Caption; case RootStorageNode.Data.PropertyType of STGTY_STORAGE: Path := Path + W + '\'; STGTY_STREAM: begin try ForceDirectories(Path); F := TFileStream.Create(Path + W, fmCreate); try GetStorageData(RootStorageNode, F); finally F.Free; end; except DebugLog.Add(Path + W); end; end; end; for I := 0 to RootStorageNode.VectorCount - 1 do begin ChildStorageNode := TStorageElement(RootStorageNode.GetVector(I).SlaveNode); if ChildStorageNode = nil then Continue; if ChildStorageNode.ID <> RootNodeIndex then Extract(Path, ChildStorageNode.ID); end; end;
function TPoifsFile.GetBlock(Adress: Integer): TPoifsBlock; var BlockOffset: Integer; begin BlockOffset := GetBlockOffset(Adress); if BlockOffset < FStream.Size then begin FStream.Position := BlockOffset; FStream.ReadBuffer(Result, SizeOf(TPoifsBlock)); end else raise Exception.Create('Wrong block offset at addres: ' + IntToStr(Adress)); end;
function TPoifsFile.ReadPropsArray: Boolean; var I, J, Len, LastGood: Integer; PropsBlock: TPoifsPropsBlock; begin Result := True; // Len := 0; // , Property Set Storage J := FHeader._sectDirStart; LastGood := J; repeat if J = FREESECT then begin FixFatEntry(LastGood, ENDOFCHAIN); Break; end; // 4 Inc(Len, 4); SetLength(FPropsArray, Len); // try PropsBlock := TPoifsPropsBlock(GetBlock(J)); except FixFatEntry(LastGood, ENDOFCHAIN); Break; end; for I := 0 to 3 do FPropsArray[Len - 4 + I] := PropsBlock[I]; LastGood := J; // FAT J := FFat[J]; if J < ENDOFCHAIN then begin FixFatEntry(LastGood, ENDOFCHAIN); Break; end; until J = ENDOFCHAIN; end;
procedure TPoifsFile.FixFatEntry(FatIndex, NewValue: Integer); var J, Offset: Integer; begin // FAT J := FatIndex mod 128; Offset := FFatOffset[FatIndex] + J * 4; // FStream.Position := Offset; FStream.WriteBuffer(NewValue, SizeOf(Integer)); end;
Source: https://habr.com/ru/post/254541/
All Articles