Reading invalid file names from a folder

I have an interesting problem. The client of our recorded voice calls from phone calls, but the file name that was provided to the recording was invalid. Below is an example file name123:123.wmv

Believe me, the Windows Media encoder created the file, and all the information is in the file, but Windows, obviously, does not recognize the file name and only displays it in the folder as 123, and the file is 0KB

Well, here it is edited from here: Thanks to Keith Miller, who pointed me in the right direction, I could write a function that would extract the names of the streams from the file and use it.

I included a working copy of how to create two data streams in a file, read the stream names and read the data from each stream. This is absolutely amazing, so I hope other people can use it too. My code ignores the main thread. If you write data to the main stream, it is best not to ignore it.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, uGeneralStuff;

type
  _FILE_STREAM_INFORMATION = record
    NextEntryOffset: cardinal;
    StreamNameLength: cardinal;
    StreamSize: int64;
    StreamAllocationSize: int64;
    StreamName: array[0..MAX_PATH] of WideChar;
  end;

  PFILE_STREAM_INFORMATION = ^_FILE_STREAM_INFORMATION;

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    InfoBlock: _FILE_STREAM_INFORMATION;
    StatusBlock : record
      Status: Cardinal;
      Information: PDWORD;
    end;

    procedure CreateFile(FileName, Info: String);
    function ReadFile(FileName: String): String;
    function ReadStreams(filename: String): TStringList;
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

function NtQueryInformationFile(FileHandle : Cardinal;
                                  IoStatusBlock : Pointer;
                                  FileInformation : Pointer;
                                  FileInformationLength : Cardinal;
                                  FileInformationClass : Cardinal
                                  ): Cardinal; stdcall; external 'ntdll.dll';
implementation

uses Math, StrUtils;
{$R *.dfm}

function TForm1.ReadStreams(filename: String): TStringList;
var
  iFH1: Integer;
  aFileName: array[0..MAX_PATH] of WideChar;
  aStreamName: String;
begin
Result := TStringList.Create;
iFH1 := FileOpen(filename, GENERIC_READ);
NtQueryInformationFile(iFH1, @StatusBlock, @InfoBlock, SizeOf(InfoBlock), 22);  // 22 Means FileStreamInformation
FileClose(iFH1);
while (1=1) do
  begin
  if InfoBlock.StreamNameLength = 0 then
    break;
  CopyMemory(@aFileName, @InfoBlock.StreamName, InfoBlock.StreamNameLength);
  aStreamName := Copy(aFileName, 1, PosEx(':', aFileName, 2) - 1);
  if aStreamName <> ':' then   //Ignore main stream, because I know I didn't write data in there
    Result.Add(aStreamName);
  if (InfoBlock.NextEntryOffset = 0) then
    break;
  InfoBlock := PFILE_STREAM_INFORMATION(PByte(@InfoBlock) + InfoBlock.NextEntryOffset)^;
  end;
end;


procedure TForm1.Button2Click(Sender: TObject);
var
  aStreams: TStringList;
  I: Integer;
begin
aStreams := ReadStreams('C:\Temp\123');
for I := 0 to aStreams.Count - 1 do
  begin
  ShowMessage(ReadFile('C:\Temp\123' + aStreams[I]));
  end;
end;

procedure TForm1.CreateFile(FileName, Info: String);
var
  iFH1: Integer;
  Buffer: PAnsiString;
begin
  iFH1 := FileCreate(FileName);
  Buffer := PAnsiString(AnsiString(Info) + #0);
  FileWrite(iFH1, Buffer^, Length(Info));
  FileClose(iFH1);
end;

function TForm1.ReadFile(FileName: String): String;
var
  iFH1: Integer;
  Buffer: PAnsiChar;
  iFL: Integer;
  iBR, iCurPos, iReadSize: Integer;
begin
  iFH1 := FileOpen(FileName, GENERIC_READ);
  iFL := FileSeek(iFH1, 0, 2);
  FileSeek(iFH1, 0, 0);
  iReadSize := Min(iFL, 1024);
  Buffer := AllocMem(iReadSize + 1);
  iCurPos := 0;
  Result := '';
  while iCurPos < iFL do
    begin
    iBR := FileRead(iFH1, Buffer^, iReadSize);
    if iBR = -1 then
      break;
    Result := Result + Buffer;
    Inc(iCurPos, iBR);
    end;
  FileClose(iFH1);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  CreateFile('C:\Temp\123:123.txt', 'This is TestFile 1');
  CreateFile('C:\Temp\123:345.txt', 'This is TestFile 2');
  ShowMessage(ReadFile('C:\Temp\123:123.txt'));
  ShowMessage(ReadFile('C:\Temp\123:345.txt'));
end;

end.
+5
source share
3 answers

Using a: in the file name creates an alternate data stream in the file. See the article at http://support.microsoft.com/kb/105763

In your example, the file is called 123, and the stream is called 123.wmv. You can write a program to extract a stream from a file and rewrite it using a common file name.

http://www.flexhex.com/docs/articles/alternate-streams.phtml .

+8

FindFirst TSearchRec . FindData (TWin32FindData), , . , .

. , ADSFindFirst (, , NtQueryInformationFile .) Delphi , : http://www.tek-tips.com/faqs.cfm?fid=7167

+1

@KeithMiller , 'C:\Temp\123' .

Delphi XE (so - Unicode!) article:

type
  NTSTATUS = Cardinal;
  TFileInformationClass = (
    FileDirectoryInformation                  = 1,
    FileFullDirectoryInformation,
    FileBothDirectoryInformation,
    FileBasicInformation,
    FileStandardInformation,
    FileInternalInformation,
    FileEaInformation,
    FileAccessInformation,
    FileNameInformation,
    FileRenameInformation,
    FileLinkInformation,
    FileNamesInformation,
    FileDispositionInformation,
    FilePositionInformation,
    FileFullEaInformation,
    FileModeInformation,
    FileAlignmentInformation,
    FileAllInformation,
    FileAllocationInformation,
    FileEndOfFileInformation,
    FileAlternateNameInformation,
    FileStreamInformation,
    FilePipeInformation,
    FilePipeLocalInformation,
    FilePipeRemoteInformation,
    FileMailslotQueryInformation,
    FileMailslotSetInformation,
    FileCompressionInformation,
    FileObjectIdInformation,
    FileCompletionInformation,
    FileMoveClusterInformation,
    FileQuotaInformation,
    FileReparsePointInformation,
    FileNetworkOpenInformation,
    FileAttributeTagInformation,
    FileTrackingInformation,
    FileIdBothDirectoryInformation,
    FileIdFullDirectoryInformation,
    FileValidDataLengthInformation,
    FileShortNameInformation,
    FileIoCompletionNotificationInformation,
    FileIoStatusBlockRangeInformation,
    FileIoPriorityHintInformation,
    FileSfioReserveInformation,
    FileSfioVolumeInformation,
    FileHardLinkInformation,
    FileProcessIdsUsingFileInformation,
    FileNormalizedNameInformation,
    FileNetworkPhysicalNameInformation,
    FileIdGlobalTxDirectoryInformation,
    FileIsRemoteDeviceInformation,
    FileAttributeCacheInformation,
    FileNumaNodeInformation,
    FileStandardLinkInformation,
    FileRemoteProtocolInformation,
    FileMaximumInformation
  );
  PIOStatusBlock = ^TIOStatusBlock;
  TIOStatusBlock = packed record
    case Boolean of
      False: (Status: NTSTATUS; P: Pointer;);
      True: (Information:  ULONG_PTR);
  end;
  PFileStreamInformation = ^TFileStreamInformation;
  TFileStreamInformation = packed record
    NextEntryOffset: ULONG;
    StreamNameLength: ULONG;
    StreamSize: LARGE_INTEGER;
    StreamAllocationSize: LARGE_INTEGER;
    StreamName: array[0..0] of Char;
  end;

type
  TNtQueryInformationFile = function(FileHandle: THandle; IoStatusBlock: PIOStatusBlock;
  FileInformation: Pointer; Length: ULONG; FileInformationClass: TFileInformationClass): NTSTATUS; stdcall;

procedure GetAlternateFileStreamNames(const FileName: string; StreamNames: TStrings);
var
  hNT, hFile: THandle;
  NtQueryInformationFile: TNtQueryInformationFile;
  Buffer: array[Word] of Byte;
  ioStatus: TIOStatusBlock;
  P: PFileStreamInformation;
  S: string;
  L: Integer;
begin
  hNT := GetModuleHandle('ntdll.dll');
  if hNT = 0 then
    Exit;
  NtQueryInformationFile := GetProcAddress(hNT, 'NtQueryInformationFile');
  if @NtQueryInformationFile = nil then
    Exit;

  FillChar(Buffer, SizeOf(Buffer), 0);
  hFile := CreateFile(PChar(FileName), 0, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
  try
    if NtQueryInformationFile(hFile, @ioStatus, @Buffer[0], SizeOf(Buffer), FileStreamInformation) = 0 then
    begin
      StreamNames.BeginUpdate;
      try
        StreamNames.Clear;
        P := @Buffer[0];
        while Assigned(P) do
        begin
          SetString(S, P^.StreamName, P^.StreamNameLength div SizeOf(Char));
          // strip trailing :$DATA
          L := Length(S);
          if (L >= 6) and (StrComp(@S[L - 5], ':$DATA') = 0) then
            Delete(S, L - 5, L);
          StreamNames.Add(S);

          if P^.NextEntryOffset = 0 then
            P := nil
          else
            P := Pointer(Integer(P) + P^.NextEntryOffset); //@Buffer[P^.NextEntryOffset];
        end;
      finally
        StreamNames.EndUpdate;
      end;
    end;
  finally
    CloseHandle(hFile);
  end;
end;


procedure TForm1.Button2Click(Sender: TObject);
var
  StreamNames: TStringList;
begin
  StreamNames := TStringList.Create;
  try
    GetAlternateFileStreamNames('C:\Temp\123', StreamNames);
    ShowMessage(StreamNames.Text);
  finally
    StreamNames.Free;
  end;
end;

, , , :

  • ':' - ,
  • ':123.txt' -
  • ':345.txt' - second alternative stream

Completely untested and strange, it must also be changed for D2007 and earlier.

+1
source

All Articles