如何将 NTFS 元数据添加到任何文件?
How can I add NTFS metadata to any file?
是否可以将“Version = 1.2.3.4”之类的信息添加到(比方说)TXT 文件?这可以通过 NTFS 元数据实现吗?如果可以,我可以通过程序设置这样的信息吗?
提前感谢您的任何提示!伯恩德
您可以使用替代数据流 (ADS), or Extended File Attributes (more e.g. here)。
如果您确定您的文件保留在 NTFS 上,替代数据流是存储任意数量替代数据的完美方式 - 但当此类文件离开 NTFS 时,您将丢失 ADS。
另一种选择是使用扩展文件属性,cross-platform、cross-FS 都支持它,但它有限制(例如可以存储多少数据)。如果您要保存例如版本信息,这可能是最好的方式。
谢谢罗伯特!我使用了您建议的 NTFS 扩展文件属性。它们被称为“文件摘要信息”。不幸的是,这些信息没有显示在 Windows 文件资源管理器的“详细信息”选项卡中 - 比方说 - 一个文本文件。我找到了注册表的一些技巧,但它们还没有用。我的代码是 Delphi:
CONST FmtID_SummaryInformation:TGUID= '{F29F85E0-4FF9-1068-AB91-08002B27B3D9}';
// FMTID_DocSummaryInformation:TGUID='{D5CDD502-2E9C-101B-9397-08002B2CF9AE}';
// FMTID_UserDefinedProperties:TGUID='{D5CDD505-2E9C-101B-9397-08002B2CF9AE}';
IID_IPropertySetStorage:TGUID= '{0000013A-0000-0000-C000-000000000046}';
STGFMT_FILE=3; //Indicates that the file must not be a compound file.
//This element is only valid when using the StgCreateStorageEx
//or StgOpenStorageEx functions to access the NTFS file system
//implementation of the IPropertySetStorage interface.
//Therefore, these functions return an error if the riid
//parameter does not specify the IPropertySetStorage interface,
//or if the specified file is not located on an NTFS file system volume.
STGFMT_ANY=4; //Indicates that the system will determine the file type and
//use the appropriate structured storage or property set
//implementation.
//This value cannot be used with the StgCreateStorageEx function.
// Summary Information
PID_TITLE = 2;
PID_SUBJECT = 3;
PID_AUTHOR = 4;
PID_KEYWORDS = 5;
PID_COMMENTS = 6;
PID_TEMPLATE = 7;
PID_LASTAUTHOR = 8;
PID_REVNUMBER = 9;
PID_EDITTIME = 10;
PID_LASTPRINTED = 11;
PID_CREATE_DTM = 12;
PID_LASTSAVE_DTM = 13;
PID_PAGECOUNT = 14;
PID_WORDCOUNT = 15;
PID_CHARCOUNT = 16;
PID_THUMBNAIL = 17;
PID_APPNAME = 18;
PID_SECURITY = 19;
(*
// Document Summary Information
PID_CATEGORY = 2;
PID_PRESFORMAT = 3;
PID_BYTECOUNT = 4;
PID_LINECOUNT = 5;
PID_PARCOUNT = 6;
PID_SLIDECOUNT = 7;
PID_NOTECOUNT = 8;
PID_HIDDENCOUNT = 9;
PID_MMCLIPCOUNT = 10;
PID_SCALE = 11;
PID_HEADINGPAIR = 12;
PID_DOCPARTS = 13;
PID_MANAGER = 14;
PID_COMPANY = 15;
PID_LINKSDIRTY = 16;
PID_CHARCOUNT2 = 17;
*)
FUNCTION IsNTFS(AFileName:AnsiString):Boolean;
VAR fso,drv:OleVariant;
BEGIN
fso:=CreateOleObject('Scripting.FileSystemObject'{=});
drv:=fso.GetDrive(fso.GetDriveName(AFileName));
Result:=drv.FileSystem='NTFS'{=};
END;
FUNCTION StgOpenStorageEx(
CONST pwcsName:POleStr; //Pointer to the path of the
//file containing storage object
grfMode:LongInt; //Specifies the access mode for the object
stgfmt:DWORD; //Specifies the storage file format
grfAttrs:DWORD; //Reserved; must be zero
pStgOptions:Pointer;//Address of STGOPTIONS pointer
reserved2:Pointer; //Reserved; must be zero
riid:PGUID; //Specifies the GUID of the interface pointer
OUT stgOpen:IStorage//Address of an interface pointer
) : HResult; stdcall; external 'ole32.dll'{=};
FUNCTION GetFileSummaryInfo(FileName:AnsiString):AnsiString;
{Read the File Summary Info of a file (NTFS)}
FUNCTION PropertyPIDToCaption(CONST ePID:Cardinal):AnsiString;
BEGIN {PropertyPIDToCaption}
CASE ePID OF
PID_TITLE: Result:='Title';
PID_SUBJECT: Result:='Subject';
PID_AUTHOR: Result:='Author';
PID_KEYWORDS: Result:='Keywords';
PID_COMMENTS: Result:='Comments';
PID_TEMPLATE: Result:='Template';
PID_LASTAUTHOR: Result:='Last Saved By';
PID_REVNUMBER: Result:='Revision Number';
PID_EDITTIME: Result:='Total Editing Time';
PID_LASTPRINTED: Result:='Last Printed';
PID_CREATE_DTM: Result:='Create Time/Date';
PID_LASTSAVE_DTM:Result:='Last Saved Time/Date';
PID_PAGECOUNT: Result:='Number of Pages';
PID_WORDCOUNT: Result:='Number of Words';
PID_CHARCOUNT: Result:='Number of Characters';
PID_THUMBNAIL: Result:='Thumbnail';
PID_APPNAME: Result:='Creating Application';
PID_SECURITY: Result:='Security';
ELSE Result:='$'+IntToHex(ePID,8);
END
END; {PropertyPIDToCaption}
VAR i,k:Integer;
PropSetStg:IPropertySetStorage;
PropSpec:ARRAY OF TPropSpec;
PropStg:IPropertyStorage;
PropVariant:ARRAY OF TPropVariant;
Rslt:HResult;
S:AnsiString;
Stg:IStorage;
PropEnum:IEnumSTATPROPSTG;
HR:HResult;
PropStat:STATPROPSTG;
AHRes:HRESULT;
PFNw,P:PWideChar;
BEGIN {GetFileSummaryInfo}
GetMem(P,257); PFNw:=StringToWideChar(FileName,P,256);
Result := '';
TRY
OleCheck(StgOpenStorageEx(PFNw,STGM_READ OR STGM_SHARE_DENY_WRITE,STGFMT_FILE,0,NIL,NIL,@IID_IPropertySetStorage,Stg));
PropSetStg:=Stg AS IPropertySetStorage;
AHRes:=PropSetStg.Open(FmtID_SummaryInformation,STGM_READ OR STGM_SHARE_EXCLUSIVE,PropStg);
IF AHRes<>S_OK THEN Exit;
OleCheck(AHRes);
OleCheck(PropStg.Enum(PropEnum));
hr:=PropEnum.Next(1,PropStat,NIL);
i:=0;
WHILE hr=S_OK DO BEGIN
inc(i);
SetLength(PropSpec,I);
PropSpec[i-1].ulKind:=PRSPEC_PROPID;
PropSpec[i-1].propid:=PropStat.propid;
hr := PropEnum.Next(1,PropStat, nil);
END;
SetLength(PropVariant,i);
Rslt:=PropStg.ReadMultiple(i,@PropSpec[0],@PropVariant[0]);
IF Rslt=S_FALSE THEN Exit;
FOR k:=0 TO i-1 DO BEGIN
S:='';
IF (PropVariant[k].vt=VT_LPWSTR) AND Assigned(PropVariant[k].pwszVal) THEN
S:=WideCharToString(PropVariant[k].pwszVal);
IF (PropVariant[k].vt=VT_LPSTR) AND Assigned(PropVariant[k].pszVal) THEN
S:=PropVariant[k].pszVal;
S:=PropertyPIDToCaption(PropSpec[k].Propid)+'='+S;
IF S<>'' THEN Result:=Result+S+#13#10;
END;
FINALLY
END;
END; {GetFileSummaryInfo}
PROCEDURE SetFileSummaryInfo(FileName,Author,Title,Subject,Keywords,Comments:AnsiString);
{Write some fields of the File Summary Info of a file (NTFS)}
VAR PropSetStg:IPropertySetStorage;
PropSpec:ARRAY OF TPropSpec;
PropStg:IPropertyStorage;
PropVariant:ARRAY OF TPropVariant;
Stg:IStorage;
PFNw,P:PWideChar;
Anz:LongInt;
BEGIN {SetFileSummaryInfo}
IF NOT IsNTFS(FileName) THEN Exit;
Anz:=5;
GetMem(P,257); PFNw:=StringToWideChar(FileName,P,256);
OleCheck(StgOpenStorageEx(PFNw,STGM_SHARE_EXCLUSIVE OR STGM_READWRITE,STGFMT_ANY,0,NIL,NIL,@IID_IPropertySetStorage,Stg));
PropSetStg:=Stg AS IPropertySetStorage;
OleCheck(PropSetStg.Create(FmtID_SummaryInformation,FmtID_SummaryInformation,PROPSETFLAG_DEFAULT,STGM_CREATE OR STGM_READWRITE OR STGM_SHARE_EXCLUSIVE,PropStg));
Setlength(PropSpec,Anz);
PropSpec[0].ulKind:=PRSPEC_PROPID;
PropSpec[0].propid:=PID_AUTHOR;
PropSpec[1].ulKind:=PRSPEC_PROPID;
PropSpec[1].propid:=PID_TITLE;
PropSpec[2].ulKind:=PRSPEC_PROPID;
PropSpec[2].propid:=PID_SUBJECT;
PropSpec[3].ulKind:=PRSPEC_PROPID;
PropSpec[3].propid:=PID_KEYWORDS;
PropSpec[4].ulKind:=PRSPEC_PROPID;
PropSpec[4].propid:=PID_COMMENTS;
SetLength(PropVariant,Anz);
PropVariant[0].vt:=VT_LPSTR;
PropVariant[0].pszVal:=PChar(Author);
PropVariant[1].vt:=VT_LPSTR;
PropVariant[1].pszVal:=PChar(Title);
PropVariant[2].vt:=VT_LPSTR;
PropVariant[2].pszVal:=PChar(Subject);
PropVariant[3].vt:=VT_LPSTR;
PropVariant[3].pszVal:=PChar(Keywords);
PropVariant[4].vt:=VT_LPSTR;
PropVariant[4].pszVal:=PChar(Comments);
OleCheck(PropStg.WriteMultiple(Anz,@PropSpec[0],@PropVariant[0],2));
PropStg.Commit(STGC_DEFAULT);
FreeMem(P);
END; {SetFileSummaryInfo}
是否可以将“Version = 1.2.3.4”之类的信息添加到(比方说)TXT 文件?这可以通过 NTFS 元数据实现吗?如果可以,我可以通过程序设置这样的信息吗?
提前感谢您的任何提示!伯恩德
您可以使用替代数据流 (ADS), or Extended File Attributes (more e.g. here)。
如果您确定您的文件保留在 NTFS 上,替代数据流是存储任意数量替代数据的完美方式 - 但当此类文件离开 NTFS 时,您将丢失 ADS。
另一种选择是使用扩展文件属性,cross-platform、cross-FS 都支持它,但它有限制(例如可以存储多少数据)。如果您要保存例如版本信息,这可能是最好的方式。
谢谢罗伯特!我使用了您建议的 NTFS 扩展文件属性。它们被称为“文件摘要信息”。不幸的是,这些信息没有显示在 Windows 文件资源管理器的“详细信息”选项卡中 - 比方说 - 一个文本文件。我找到了注册表的一些技巧,但它们还没有用。我的代码是 Delphi:
CONST FmtID_SummaryInformation:TGUID= '{F29F85E0-4FF9-1068-AB91-08002B27B3D9}';
// FMTID_DocSummaryInformation:TGUID='{D5CDD502-2E9C-101B-9397-08002B2CF9AE}';
// FMTID_UserDefinedProperties:TGUID='{D5CDD505-2E9C-101B-9397-08002B2CF9AE}';
IID_IPropertySetStorage:TGUID= '{0000013A-0000-0000-C000-000000000046}';
STGFMT_FILE=3; //Indicates that the file must not be a compound file.
//This element is only valid when using the StgCreateStorageEx
//or StgOpenStorageEx functions to access the NTFS file system
//implementation of the IPropertySetStorage interface.
//Therefore, these functions return an error if the riid
//parameter does not specify the IPropertySetStorage interface,
//or if the specified file is not located on an NTFS file system volume.
STGFMT_ANY=4; //Indicates that the system will determine the file type and
//use the appropriate structured storage or property set
//implementation.
//This value cannot be used with the StgCreateStorageEx function.
// Summary Information
PID_TITLE = 2;
PID_SUBJECT = 3;
PID_AUTHOR = 4;
PID_KEYWORDS = 5;
PID_COMMENTS = 6;
PID_TEMPLATE = 7;
PID_LASTAUTHOR = 8;
PID_REVNUMBER = 9;
PID_EDITTIME = 10;
PID_LASTPRINTED = 11;
PID_CREATE_DTM = 12;
PID_LASTSAVE_DTM = 13;
PID_PAGECOUNT = 14;
PID_WORDCOUNT = 15;
PID_CHARCOUNT = 16;
PID_THUMBNAIL = 17;
PID_APPNAME = 18;
PID_SECURITY = 19;
(*
// Document Summary Information
PID_CATEGORY = 2;
PID_PRESFORMAT = 3;
PID_BYTECOUNT = 4;
PID_LINECOUNT = 5;
PID_PARCOUNT = 6;
PID_SLIDECOUNT = 7;
PID_NOTECOUNT = 8;
PID_HIDDENCOUNT = 9;
PID_MMCLIPCOUNT = 10;
PID_SCALE = 11;
PID_HEADINGPAIR = 12;
PID_DOCPARTS = 13;
PID_MANAGER = 14;
PID_COMPANY = 15;
PID_LINKSDIRTY = 16;
PID_CHARCOUNT2 = 17;
*)
FUNCTION IsNTFS(AFileName:AnsiString):Boolean;
VAR fso,drv:OleVariant;
BEGIN
fso:=CreateOleObject('Scripting.FileSystemObject'{=});
drv:=fso.GetDrive(fso.GetDriveName(AFileName));
Result:=drv.FileSystem='NTFS'{=};
END;
FUNCTION StgOpenStorageEx(
CONST pwcsName:POleStr; //Pointer to the path of the
//file containing storage object
grfMode:LongInt; //Specifies the access mode for the object
stgfmt:DWORD; //Specifies the storage file format
grfAttrs:DWORD; //Reserved; must be zero
pStgOptions:Pointer;//Address of STGOPTIONS pointer
reserved2:Pointer; //Reserved; must be zero
riid:PGUID; //Specifies the GUID of the interface pointer
OUT stgOpen:IStorage//Address of an interface pointer
) : HResult; stdcall; external 'ole32.dll'{=};
FUNCTION GetFileSummaryInfo(FileName:AnsiString):AnsiString;
{Read the File Summary Info of a file (NTFS)}
FUNCTION PropertyPIDToCaption(CONST ePID:Cardinal):AnsiString;
BEGIN {PropertyPIDToCaption}
CASE ePID OF
PID_TITLE: Result:='Title';
PID_SUBJECT: Result:='Subject';
PID_AUTHOR: Result:='Author';
PID_KEYWORDS: Result:='Keywords';
PID_COMMENTS: Result:='Comments';
PID_TEMPLATE: Result:='Template';
PID_LASTAUTHOR: Result:='Last Saved By';
PID_REVNUMBER: Result:='Revision Number';
PID_EDITTIME: Result:='Total Editing Time';
PID_LASTPRINTED: Result:='Last Printed';
PID_CREATE_DTM: Result:='Create Time/Date';
PID_LASTSAVE_DTM:Result:='Last Saved Time/Date';
PID_PAGECOUNT: Result:='Number of Pages';
PID_WORDCOUNT: Result:='Number of Words';
PID_CHARCOUNT: Result:='Number of Characters';
PID_THUMBNAIL: Result:='Thumbnail';
PID_APPNAME: Result:='Creating Application';
PID_SECURITY: Result:='Security';
ELSE Result:='$'+IntToHex(ePID,8);
END
END; {PropertyPIDToCaption}
VAR i,k:Integer;
PropSetStg:IPropertySetStorage;
PropSpec:ARRAY OF TPropSpec;
PropStg:IPropertyStorage;
PropVariant:ARRAY OF TPropVariant;
Rslt:HResult;
S:AnsiString;
Stg:IStorage;
PropEnum:IEnumSTATPROPSTG;
HR:HResult;
PropStat:STATPROPSTG;
AHRes:HRESULT;
PFNw,P:PWideChar;
BEGIN {GetFileSummaryInfo}
GetMem(P,257); PFNw:=StringToWideChar(FileName,P,256);
Result := '';
TRY
OleCheck(StgOpenStorageEx(PFNw,STGM_READ OR STGM_SHARE_DENY_WRITE,STGFMT_FILE,0,NIL,NIL,@IID_IPropertySetStorage,Stg));
PropSetStg:=Stg AS IPropertySetStorage;
AHRes:=PropSetStg.Open(FmtID_SummaryInformation,STGM_READ OR STGM_SHARE_EXCLUSIVE,PropStg);
IF AHRes<>S_OK THEN Exit;
OleCheck(AHRes);
OleCheck(PropStg.Enum(PropEnum));
hr:=PropEnum.Next(1,PropStat,NIL);
i:=0;
WHILE hr=S_OK DO BEGIN
inc(i);
SetLength(PropSpec,I);
PropSpec[i-1].ulKind:=PRSPEC_PROPID;
PropSpec[i-1].propid:=PropStat.propid;
hr := PropEnum.Next(1,PropStat, nil);
END;
SetLength(PropVariant,i);
Rslt:=PropStg.ReadMultiple(i,@PropSpec[0],@PropVariant[0]);
IF Rslt=S_FALSE THEN Exit;
FOR k:=0 TO i-1 DO BEGIN
S:='';
IF (PropVariant[k].vt=VT_LPWSTR) AND Assigned(PropVariant[k].pwszVal) THEN
S:=WideCharToString(PropVariant[k].pwszVal);
IF (PropVariant[k].vt=VT_LPSTR) AND Assigned(PropVariant[k].pszVal) THEN
S:=PropVariant[k].pszVal;
S:=PropertyPIDToCaption(PropSpec[k].Propid)+'='+S;
IF S<>'' THEN Result:=Result+S+#13#10;
END;
FINALLY
END;
END; {GetFileSummaryInfo}
PROCEDURE SetFileSummaryInfo(FileName,Author,Title,Subject,Keywords,Comments:AnsiString);
{Write some fields of the File Summary Info of a file (NTFS)}
VAR PropSetStg:IPropertySetStorage;
PropSpec:ARRAY OF TPropSpec;
PropStg:IPropertyStorage;
PropVariant:ARRAY OF TPropVariant;
Stg:IStorage;
PFNw,P:PWideChar;
Anz:LongInt;
BEGIN {SetFileSummaryInfo}
IF NOT IsNTFS(FileName) THEN Exit;
Anz:=5;
GetMem(P,257); PFNw:=StringToWideChar(FileName,P,256);
OleCheck(StgOpenStorageEx(PFNw,STGM_SHARE_EXCLUSIVE OR STGM_READWRITE,STGFMT_ANY,0,NIL,NIL,@IID_IPropertySetStorage,Stg));
PropSetStg:=Stg AS IPropertySetStorage;
OleCheck(PropSetStg.Create(FmtID_SummaryInformation,FmtID_SummaryInformation,PROPSETFLAG_DEFAULT,STGM_CREATE OR STGM_READWRITE OR STGM_SHARE_EXCLUSIVE,PropStg));
Setlength(PropSpec,Anz);
PropSpec[0].ulKind:=PRSPEC_PROPID;
PropSpec[0].propid:=PID_AUTHOR;
PropSpec[1].ulKind:=PRSPEC_PROPID;
PropSpec[1].propid:=PID_TITLE;
PropSpec[2].ulKind:=PRSPEC_PROPID;
PropSpec[2].propid:=PID_SUBJECT;
PropSpec[3].ulKind:=PRSPEC_PROPID;
PropSpec[3].propid:=PID_KEYWORDS;
PropSpec[4].ulKind:=PRSPEC_PROPID;
PropSpec[4].propid:=PID_COMMENTS;
SetLength(PropVariant,Anz);
PropVariant[0].vt:=VT_LPSTR;
PropVariant[0].pszVal:=PChar(Author);
PropVariant[1].vt:=VT_LPSTR;
PropVariant[1].pszVal:=PChar(Title);
PropVariant[2].vt:=VT_LPSTR;
PropVariant[2].pszVal:=PChar(Subject);
PropVariant[3].vt:=VT_LPSTR;
PropVariant[3].pszVal:=PChar(Keywords);
PropVariant[4].vt:=VT_LPSTR;
PropVariant[4].pszVal:=PChar(Comments);
OleCheck(PropStg.WriteMultiple(Anz,@PropSpec[0],@PropVariant[0],2));
PropStg.Commit(STGC_DEFAULT);
FreeMem(P);
END; {SetFileSummaryInfo}