我有一个有趣的问题。我们的客户通过电话录制了语音对话,但是录制的文件名无效。以下是文件名123:123.wmv
相信它,Windows Media编码器创建了该文件,所有信息都在文件中,但Windows显然无法识别文件名,只显示在文件夹中123
,文件为0KB
从这里开始编辑:感谢Keith Miller向我指出正确的方向,我可以编写一个函数,从文件中提取流名称并使用它。
我已经包含了如何在文件中创建两个数据流,读取流名称和从每个流中读取数据的工作副本。这非常棒,所以我希望其他人也可以使用它。 我的代码忽略了主流。如果您将数据写入主流,最好不要忽略它。
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 :(得分:8)
在文件名中使用:在文件中创建备用数据流。请参阅文章 http://support.microsoft.com/kb/105763
在您的示例中,文件名为123,流称为123.wmv。您可以编写一个程序来从文件中提取流,并使用传统的文件名重写它。
http://www.flexhex.com/docs/articles/alternate-streams.phtml上的文章应该有所帮助。
答案 1 :(得分:1)
FindFirst
使用TSearchRec
记录来返回文件属性。你有一个FindData
元素(TWin32FindData
),它包含一些额外的属性,比如文件的备用名称。也许你可以使用它。
编辑:我找到了一个页面,其中包含一个名为ADSFindFirst
的函数的单元(其中包含NtQueryInformationFile
整齐地包含在内部。)我没有德尔福在这里看起来很有希望:http://www.tek-tips.com/faqs.cfm?fid=7167
答案 2 :(得分:1)
作为@KeithMiller answered,您正在使用空主流和两个备用流创建文件'C:\Temp\123'
。
我快速尝试使用Delphi XE(所以 - 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'
- 第二个备用流完全未经测试和奇怪,也需要针对D2007及更早版本进行修改。