2012-05-10 3 views
5

흥미로운 문제가 있습니다. 우리 고객이 전화 통화로 음성 대화를 녹음했지만 녹음 파일에 지정된 파일 이름이 유효하지 않습니다. 여기 123:123.wmv폴더에서 잘못된 FileNames 읽기.

이 믿거 파일 이름의 예는, 윈도우 미디어 인코더는 파일을 생성하고, 모든 정보는하지만 Windows가 분명히 파일 이름을 인식하지 않으며, 단지 123 같은 폴더에 표시, 파일에 파일이 0KB입니다

글쎄, 여기에서 그것은 편집됩니다 : 올바른 방향으로 나를 가리킨 키이스 밀러 덕분에 파일에서 스트림 이름을 추출하고 그것을 사용하는 함수를 작성할 수 있습니다.

두 개의 데이터 스트림을 파일로 작성하고 스트림 이름을 읽고 각 스트림에서 데이터를 읽는 방법에 대한 작업 사본을 포함했습니다. 이것은 정말 끝내 주므로 다른 사람들도 이것을 사용할 수 있기를 바랍니다. 내 코드가 기본 스트림을 무시합니다. 기본 스트림에 데이터를 쓰는 경우 무시하지 않는 것이 가장 좋습니다.

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. 
+0

큰 따옴표로 파일 이름을 넣으려고 했습니까?위의 예제 중 하나를 사용하여 파일을'' "C : \ Temp \ 123 : 345.txt" ''로 참조하십시오. –

+0

@ Jerry, Jaque는 이미 파일을 여는 것이 문제가 아니라고 말했습니다. 문제는 파일의 이름 *을 발견하는 것입니다. Explorer와'FindFirstFile' 모두 나머지 이름없이 "123"만 표시합니다. 무엇을 넣을 지 모를 때는 따옴표 안에 넣을 수 없습니다. –

+0

미안하지만 나는 그것을 잡지 않았다. 자,'FindFirstFileEx()'의 유니 코드 버전을 사용해 보셨습니까? 찾고자하는 패턴의 처음에''\\? \ ''를 두는 것을 잊지 마십시오 (모든 파일에 대해''\\? \ *' '). –

답변

8

파일 이름에 a :를 사용하면 파일에 대체 데이터 스트림이 생성됩니다. http://support.microsoft.com/kb/105763

예제에서 파일 이름은 123이고 스트림 이름은 123.wmv입니다. 파일에서 스트림을 추출하고이를 일반 파일 이름으로 다시 쓰는 프로그램을 작성할 수 있습니다.

http://www.flexhex.com/docs/articles/alternate-streams.phtml의 도움말이 도움이됩니다.

+1

'사본 123 : 123.wmv ​​123.wmv'이 작업을 끝낼 것 –

+0

@ 데이비드 - 우수! 명령 줄이 GUI보다 더 강력하다는 것을 보여주기 위해 이동합니다. –

+0

@DavidHeffernan 문제는 David입니다. 전체 파일 이름이 무엇인지 알아야합니다. – Jaques

1

FindFirst는 파일 속성을 반환하는 TSearchRec 기록을 사용합니다. 여기에는 파일의 대체 이름과 같은 몇 가지 추가 속성이 포함 된 FindData 요소 ( TWin32FindData)가 있습니다. 아마 당신은 그것을 사용할 수 있습니다.

편집 : 나는 ADSFindFirst라는 기능 단위를 포함하는 페이지 발견 (incidently 포함, NtQueryInformationFile 깔끔하게 내부적으로 감쌌다.) 여기 델파이가 있지만 약속 같은데하지 않습니다 http://www.tek-tips.com/faqs.cfm?fid=7167

+0

"대체 이름"은 * Program Files *가 * Progra ~ 1 *이되는 짧은 파일 이름입니다. Jaques가 찾고있는 것을 나열하는 데 도움이되지 않습니다. –

+0

시도해보십시오. 더 이상 정보를주지 않겠습니다 – Jaques

+0

예, 그는 정확한 이름이 필요하다고 말했지만 그가 왜 필요하다고 말하지 않았습니다. 표시 목적으로 만 사용하는 경우에만 표시됩니다. 그러나 그가 다른 곳에서 그것을 열 수 있도록 문자열 형태로 필요하다면 도움이 될 것입니다. –

1

@KeithMiller answered과 마찬가지로 비어있는 기본 스트림과 두 개의 대체 스트림이있는 'C:\Temp\123' 파일을 만듭니다.

델파이 XE에서 내 빠른 시도가 (그래서 - 유니 코드!) 스트림의 이름을 표시하는 코드의 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; 

, 그것은 보여줍니다 다음 항목 :

  1. ':' - 이름이 메인 스트림, 생각
  2. ':123.txt' - 첫번째로 대체 스트림
  3. ':345.txt' - 두 번째 대체 스트림

완전히 테스트되지 않은 상태이며 이상하게도 D2007 이하로 수정해야합니다.

관련 문제