Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Erweiterte Dateiinformationen schreiben (https://www.delphipraxis.net/20244-erweiterte-dateiinformationen-schreiben.html)

Luckie 15. Apr 2004 02:43


Erweiterte Dateiinformationen schreiben
 
Unter NTFS Dateisystemen kann man noch zusätzlich Dateiinformnationen zu den Dateien Hinterlegen. Zum Beispiel ein Kommentar oder den Autor und sonstige Informationen. Im Internet habe ich jetzt schon Code gefunden diese auszulesen:
http://www.howtodothings.com/showart...sp?article=447
Nur leider fehlende so ziemllich alle Kommentare. Zum Laufen habe ich ihn gebracht nur verstehe ich nicht, was da passiert. Wenn mir da mal jemand auf die Sprünge helfen könnte.

Desweiteren will ich mit meinem Programm diese Infos setzen können. Also ich gebe die Datei an, welche Information ich setzten möchte und einen String mit der Information. Leider habe ich davon keinen Plan, wie ich das hinbekommen könnte.

Habe was gefunden: http://www.delphipages.com/news/detaildocs.cfm?ID=104

Aber was da genau passiert, hätte ich trotzdem gerne erklärt bekommen. :gruebel:

Hier nun meine Umsetzung des Codes: FileComment. Ich habe mich dabei auf das Setzen und Auslesen des Kommentares beschränkt.

mumu 15. Apr 2004 12:51

Re: Erweiterte Dateiinformationen schreiben
 
was willste denn genau wissen? welche zeilen verstehst du nicht? ich find den code eigentlich nicht so schwer.

Luckie 15. Apr 2004 12:57

Re: Erweiterte Dateiinformationen schreiben
 
Wie wäre es mit denen hier:
Delphi-Quellcode:
  OleCheck(StgOpenStorageEx(PWideChar(FileName),
  STGM_READ or STGM_SHARE_DENY_WRITE,
  STGFMT_FILE,
  0, nil, nil, @IID_IPropertySetStorage, stg));

  PropSetStg := Stg as IPropertySetStorage;

  OleCheck(PropSetStg.Open(FmtID_SummaryInformation,
     STGM_READ or STGM_SHARE_EXCLUSIVE, PropStg));

  OleCheck(PropStg.Enum(PropEnum));
Und der Schleife:
Delphi-Quellcode:
  hr := PropEnum.Next(1, PropStat, nil);
   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;

mirage228 15. Apr 2004 13:06

Re: Erweiterte Dateiinformationen schreiben
 
Zitat:

Zitat von Luckie
Wie wäre es mit denen hier:
Delphi-Quellcode:
  OleCheck(StgOpenStorageEx(PWideChar(FileName),
  STGM_READ or STGM_SHARE_DENY_WRITE,
  STGFMT_FILE,
  0, nil, nil, @IID_IPropertySetStorage, stg));

  PropSetStg := Stg as IPropertySetStorage;

  OleCheck(PropSetStg.Open(FmtID_SummaryInformation,
     STGM_READ or STGM_SHARE_EXCLUSIVE, PropStg));

  OleCheck(PropStg.Enum(PropEnum));
Und der Schleife:
Delphi-Quellcode:
  hr := PropEnum.Next(1, PropStat, nil);
   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;

Hi,
hier einige Erklärungen ;)
  • OleCheck löst eine EOleSysError-Exception aus, wenn der Ergebniscode einen Fehler anzeigt. (siehe OH)
  • PropSetStg := Stg as IPropertySetStorage -> Stg wird zum Interface IPropertySetStorage gecastet (ist wahrs. vorher IInterface) ("To get a pointer to the NTFS implementation of IPropertySetStorage, we have to call the StgCreateStorageEx function:")
  • Die Schleife wird sooft wiederholt, bis PropEnum.Next nicht Ok zurückgibt, sprich es keine Eigenschaften mehr zu setzten gibt.

mfG
mirage228

Luckie 15. Apr 2004 13:07

Re: Erweiterte Dateiinformationen schreiben
 
So weit bin ich auch schon gekoommen. Das reicht mir aber nicht.

mirage228 15. Apr 2004 13:14

Re: Erweiterte Dateiinformationen schreiben
 
Zitat:

Zitat von Luckie
So weit bin ich auch schon gekoommen. Das reicht mir aber nicht.

Was möchtest du denn noch wissen? Die Deklaration der Interface?

Bei OleCheck und der Schleife gibt es aber nicht mehr allzu viel zu erklären.

mfG
mirage228

schitho 15. Apr 2004 21:23

Re: Erweiterte Dateiinformationen schreiben
 
Hi,

ich hab mir die Unit1 von FileComment angesehen.

So jetzt kommen ein paar typische Anfängerfragen (ersuche schon jetzt dafür um Nachsicht): :oops:

Mit

Delphi-Quellcode:
SetFileSummaryInfo('c:\tmp\test.txt,PID_COMMENTS,'Hier könnte ein Kommentar stehen')
setze ich einen Kommentar.

Nun zu meinen ersten Fragen:
  • Die Funktion liefert einen Rückwert. Welche Information beinhaltet dieser Rückgabewert? Wann war alles OK?
  • Wie rufe ich die Funktion auf, wenn ich in mehrere Felder schreiben möchte (z.B. Kommentar und Autor)?
  • Ich hab keine Möglichkeit gefunden die Quelle und Revisonsnummer zu ändern. Geht das wirklich nicht?

Nun zum Auslesen:

Mit
Delphi-Quellcode:
GetFileSummaryInfo('c:\tmp\test.txt')
liefert den Kommentar in einem String zurück.
  • Wie kann man die anderen Felder auslesen?
Wär super-nett, wenn Ihr mir da weiterhelfen könntet.

Danke!

Gruß
Thomas

Luckie 15. Apr 2004 21:50

Re: Erweiterte Dateiinformationen schreiben
 
Delphi-Quellcode:
function SetFileSummaryInfo(const FileName: WideString; PID_InfoType: Cardinal;
  InfoStr: string): DWORD;
Ich dachte das wäre klar. Wenn du gibst mit PID_Infotype (mir ist nichts besseres um halb fünf morgens eingefallen) das Feld an in das du schreiben willst. Mögliche Werte:
PID_AUTHOR
PID_TITLE
PID_SUBJECT
PID_KEYWORDS
PID_COMMENTS

Sie liefert null wenn alles geklappt hat oder wenn was nicht geklappt hatt sollte sie den letzten Systemfehler des Prozesses zurückgeben.

In mehrer Felder schreiben geht nicht, entweder du schreibst sie dir selber um, dass sie es kann oder rufst sie entsprechend oft nacheinander auf. Ich habe daraurf verzichtet mir diese Arbeit zu machen, weil es mir von Anfang an eigentlich nur um den Kommentar ging. (Alle anderen Felder erscheinen mir sowieso unwichtig.)

Auslesen? Na kuck es dir an:
Delphi-Quellcode:
S := PropVariant[k].pwszVal;
if PropSpec[k].propid = PID_COMMENTS then
  Result := S;
Hier lasse ich mir nur den Wert von s zurückgeben wenn die PropID PID_COMMENTS entspricht. So, denk nach und mach was draus. ;)

schitho 15. Apr 2004 22:50

Re: Erweiterte Dateiinformationen schreiben
 
Hallo Luckie!

Danke für Deine geduldige Antwort!

Zitat:

Zitat von Luckie
Ich dachte das wäre klar. Wenn du gibst mit PID_Infotype (mir ist nichts besseres um halb fünf morgens eingefallen) das Feld an in das du schreiben willst. Mögliche Werte:
PID_AUTHOR
PID_TITLE
PID_SUBJECT
PID_KEYWORDS
PID_COMMENTS

Sie liefert null wenn alles geklappt hat oder wenn was nicht geklappt hatt sollte sie den letzten Systemfehler des Prozesses zurückgeben.

In mehrer Felder schreiben geht nicht, entweder du schreibst sie dir selber um, dass sie es kann oder rufst sie entsprechend oft nacheinander auf. Ich habe daraurf verzichtet mir diese Arbeit zu machen, weil es mir von Anfang an eigentlich nur um den Kommentar ging. (Alle anderen Felder erscheinen mir sowieso unwichtig.)

So hätte ich es eh auch verstanden. Doch irgendwie funktioniert das nicht. Sobald ich einen Wert (z.B. PID_COMMENTS) schreibe funktioniert das. Schreib ich aber noch einen (z.B. PID_TITLE), indem ich die Funktion nochmals aufrufe, verschwindet der vorige Eintrag wieder. :cry:

Somit funktioniert der mehrmalige Aufruf nicht (zumindest bei mir).

Außerdem hab ich keine Möglichkeit gefunden, wie ich das Feld Revisionsnummer und Quelle mit Daten füllen kann. :?

Zitat:

Zitat von Luckie
Auslesen? Na kuck es dir an:
Delphi-Quellcode:
S := PropVariant[k].pwszVal;
if PropSpec[k].propid = PID_COMMENTS then
  Result := S;
Hier lasse ich mir nur den Wert von s zurückgeben wenn die PropID PID_COMMENTS entspricht. So, denk nach und mach was draus. ;)

Ups. Das hab ich übersehen. :oops:

Danke für Deine prompte Antwort.

Gruß
Thomas

MathiasSimmack 27. Apr 2004 20:06

Re: Erweiterte Dateiinformationen schreiben
 
Ich muss mal diesen Beitrag aus der Versenkung holen, weil mich ein paar Dinge interessieren -

Ich nehme mal an, dass du, @Luckie, die Funktion "GetFileSummaryInfo" so beschnitten hast, dass sie grundsätzlich den Kommentar zurückliefert. Ich wollte mir auch andere Werte anzeigen lassen und musste die Funktion dafür erst einmal öffnen. Dabei ist mir aufgefallen, dass die Funktion nichts anzeigt, wenn man die gewünschte Eigenschaft vorher mit Bordmitteln (sprich: Rechtsklick auf die Datei -> Eigenschaften -> Dateiinfos) setzt.

Erst wenn ich bspw. mit dem Programm den Kommentar eintragen lasse, wird er hinterher von "GetFileSummaryInfo" auch angezeigt. Der Witz dabei: Wenn ich jetzt andere Eigenschaften über den Windows-Dialog eintrage, dann werden mir die auch von der Funktion angezeigt. Das wäre das erste Problem, über das man diskutieren müsste. @mirage: Vorschläge? :mrgreen:


Der zweite Punkt ist kein Problem sondern ein Vorschlag. Die Funktion aus deinem Programm, @Luckie, lässt sich nämlich wie folgt verkürzen
Delphi-Quellcode:
function GetFileSummaryInfo(const FileName: WideString;
  GUID_SummaryType: TGUID; PID_InfoType: cardinal): WideString;
var
  Stg        : IStorage;
  PropSetStg : IPropertySetStorage;
  PropStg    : IPropertyStorage;
  PropSpec   : TPropSpec;
  PropVariant : TPropVariant;
begin
  Result     := '';

  if(StgOpenStorageEx(pwidechar(FileName),STGM_READ or STGM_SHARE_DENY_WRITE,
    STGFMT_FILE,0,nil,nil,@IID_IPropertySetStorage,Stg) = S_OK) then
  begin
    PropSetStg := Stg as IPropertySetStorage;

    if(PropSetStg.Open(GUID_SummaryType,STGM_READ or
      STGM_SHARE_EXCLUSIVE,PropStg) = S_OK) then
    begin
      PropSpec.ulKind := PRSPEC_PROPID;
      PropSpec.propid := PID_InfoType;

      if(PropStg.ReadMultiple(1,@PropSpec,@PropVariant) = S_OK) and
        (PropVariant.vt = VT_LPWSTR) and
        (PropVariant.pwszVal <> nil) then
      Result := PropVariant.pwszVal;
    end;
  end;
end;
und funktioniert trotzdem noch. ;) Vorteil: Man kann auch die gewünschte GUID übergeben, um bspw. die Kategorie auszulesen:
Delphi-Quellcode:
dummy := GetFileSummaryInfo('MeineDatei.txt',FMTID_DocSummaryInformation,
  PID_CATEGORY);
MessageBoxW(self.handle,pwidechar(dummy),nil,0);
Weiter -

In der Funktion "SetFileSummaryInfo" gibt es IMHO das Problem, dass
Delphi-Quellcode:
OleCheck(PropSetStg.Create(FmtID_SummaryInformation, FmtID_SummaryInformation,
  PROPSETFLAG_DEFAULT,
  STGM_CREATE or STGM_READWRITE or STGM_SHARE_EXCLUSIVE, PropStg));
verwendet wurde. Laut PSDK wird durch "Create" ein neues Property-Set angelegt, und das hat zur Folge, dass die anderen Werte (in dem Fall Autor, Titel und Betreff) gelöscht werden. Nach ein bisschen Herumspielen bin ich der Meinung, dass die Funktion besser so aussehen sollte:
Delphi-Quellcode:
function SetFileSummaryInfo(const FileName: WideString; GUID_InfoType: TGUID;
  PID_InfoType: Cardinal; InfoStr: WideString): boolean;
var
  PropSetStg : IPropertySetStorage;
  PropSpec   : TPropSpec;
  hr         : HRESULT;
  PropStg    : IPropertyStorage;
  PropVariant : TPropVariant;
  Stg        : IStorage;
begin
  Result := false;

  if(StgOpenStorageEx(pwidechar(FileName),STGM_SHARE_EXCLUSIVE or
    STGM_READWRITE,STGFMT_ANY,0,nil,nil,@IID_IPropertySetStorage,stg) =
    S_OK) then
  begin
    PropSetStg := Stg as IPropertySetStorage;

    // try to open an existing Property set
    hr        := PropSetStg.Open(GUID_InfoType,STGM_READWRITE or
      STGM_SHARE_EXCLUSIVE,PropStg);

    // create a new set
    if(hr <> S_OK) then
      hr      := PropSetStg.Create(GUID_InfoType,GUID_InfoType,
        PROPSETFLAG_DEFAULT,STGM_CREATE or STGM_READWRITE or
        STGM_SHARE_EXCLUSIVE,PropStg);

    if(hr = S_OK) then
    begin
      PropSpec.ulKind    := PRSPEC_PROPID;
      PropSpec.propid    := PID_InfoType;
      PropVariant.vt     := VT_LPWSTR;
      PropVariant.pwszVal := PWideChar(InfoStr);

      if(PropStg.WriteMultiple(1,@PropSpec,@PropVariant,2) = S_OK) then
      begin
        Result := (PropStg.Commit(STGC_DEFAULT) = S_OK);
      end;
    end;
  end;
end;
Damit bleiben die alten Werte erhalten, und man kann auch wieder andere Eigenschaften wählen, indem man die passende GUID angibt (s. oben beim Beispiel der Kategorie). Und der Rückgabewert ist gleich vom bool-Typ.

So, das war´s erst mal von meiner Seite.

Assarbad 28. Apr 2004 09:14

Re: Erweiterte Dateiinformationen schreiben
 
Hi Mathias,

ich hatte ja noch nicht in Luckies Implementierung reingeschaut, aber ich persönlich würde das am ehesten über ADS machen. Die Native API erlaubt dir genau dies. Ich habe 2 Exemplare der Native API Reference, wenn du willst, kann ich dir eine vorbeibringen. Ich würde dir natürlich bei der Ausarbeitung helfen. Auch wenn mich ADS nicht so primär interessieren, kann eine Umsetzung dahingehend schon interessant sein :) ... wie zB auch meine Hardlink-Geschichte.

MathiasSimmack 28. Apr 2004 09:16

Re: Erweiterte Dateiinformationen schreiben
 
Liste der Anhänge anzeigen (Anzahl: 1)
So, noch ein Nachtrag meinerseits:

Zitat:

Zitat von MathiasSimmack
Dabei ist mir aufgefallen, dass die Funktion nichts anzeigt, wenn man die gewünschte Eigenschaft vorher mit Bordmitteln (sprich: Rechtsklick auf die Datei -> Eigenschaften -> Dateiinfos) setzt.

Mittlerweile weiß ich auch warum. Windows legt diese Einträge nicht als WideString sondern nur als String ab. Ändere ich die Funktion "GetFileSummaryInfo" entsprechend um, sehe ich das auch. ;)

Zitat:

Zitat von Luckie
In mehrer Felder schreiben geht nicht, entweder du schreibst sie dir selber um, dass sie es kann oder rufst sie entsprechend oft nacheinander auf.

Das klappt ja im Original wg. "Create" nicht, aber ich war mal so frei, eine zusätzliche Funktion "SetMultipleFileSummaryInfo" zu schreiben, die sich wie folgt aufrufen lässt:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  mal : TMultipleArrayList;
begin
  SetLength(mal,3);
  mal[0].pidInfoType := PIDSI_AUTHOR;
  mal[0].pidInfoStr := 'Ich';
  mal[1].pidInfoType := PIDSI_COMMENTS;
  mal[1].pidInfoStr := 'Tralala';
  mal[2].pidInfoType := PIDSI_TITLE;
  mal[2].pidInfoStr := 'Mein Titel';

  SetMultipleFileSummaryInfo('MeineDatei.txt',
    FMTID_SummaryInformation,
    mal);

  SetLength(mal,0);
end;
Die bearbeitete Unit (storage.pas) hänge ich ran, damit ihr das testen könnt. Die Grundlage ist zwar von Luckie, aber die Bezeichner für die Konstanten usw. stammen jetzt aus dem PSDK; aus der Datei "PropIdl.h" um genau zu sein)

Zitat:

Zitat von schitho
Außerdem hab ich keine Möglichkeit gefunden, wie ich das Feld Revisionsnummer und Quelle mit Daten füllen kann.

Erstere ist PIDSI_REVNUMBER und lässt sich recht einfach setzen
Delphi-Quellcode:
  SetFileSummaryInfo('MeineDatei.txt',
    FMTID_SummaryInformation,
    PIDSI_REVNUMBER,
    '3.0');
Dagegen gehört die Quelle (PIDMSI_SOURCE) zur Kategorie FMTID_MediaFileSummaryInformation, und die GUID war nicht einfach zu finden. *schwitz* Aber ich habe sie ja doch entdeckt (s. Unit im Anhang). ;) Und hier das Beispiel
Delphi-Quellcode:
  SetFileSummaryInfo('MeineDatei.txt',
    FMTID_MediaFileSummaryInformation,
    PIDMSI_SOURCE,
    'www.delphipraxis.net');
btw: Die Funktionen sind bisher nur darauf ausgelegt, Strings zu lesen und zu schreiben. Einige der Konstanten besitzen aber ein anderes Format. In der Unit steht es dahinter (steht nichts da, hat die Konstanten den gleichen Typ wie die in der Zeile zuvor, usw.). Ihr müsstet die Funktionen also umschreiben um solche Werte auslesen zu können. Ich wollt´s nur gesagt haben. ;)

MathiasSimmack 28. Apr 2004 09:19

Re: Erweiterte Dateiinformationen schreiben
 
Hey, da hat sich ja einer reingedrängelt.

Zitat:

Zitat von Assarbad
[...] aber ich persönlich würde das am ehesten über ADS machen. Die Native API erlaubt dir genau dies. Ich habe 2 Exemplare der Native API Reference, wenn du willst, kann ich dir eine vorbeibringen.

Anschauen würde ich mir das gern mal. Aber damit du nicht ständig CDs versaust (wie viele schulde ich dir jetzt eigentlich schon?) würde ich eine Email vorziehen. ;) Aber davon abgesehen würde mich mal interessieren, wie sich die Unit von mir auf deinem NT4 macht. Kannst du mal ein kleines Beispielprojekt zusammenschrauben und spaßeshalber ein paar Eigenschaften setzen und lesen? Das wäre sehr nett.

MathiasSimmack 22. Mai 2004 22:13

Re: Erweiterte Dateiinformationen schreiben
 
Liste der Anhänge anzeigen (Anzahl: 1)
So, ich habe die Unit noch mal poliert. Es klappt jetzt auch unter Windows 9x. Eben getestet mit einer DOC-Datei. Natürlich macht es IMHO nur mit solchen Dateien Sinn, die auch über die entsprechenden Infos verfügen. Aber evtl. klappt es jetzt auch mit den "normalen" Dateiinfos unter NT4. Ich hänge die neue Unit mal ran.

@Assarbad: Ihr Auftritt, mein Herr! :)

ringli 15. Okt 2004 20:25

Re: Erweiterte Dateiinformationen schreiben
 
Hallo!

Ich hätte mal noch eine Frage zu den erweiterten Dateiinformationen. Wie bekomme ich mit Hilfe der von MathiasSimmack bereits angehängten Unit die "$DATA"-Streams (also den Teil wo z.B. auch die "Zone Identfier" hinterlegt ist) ausgelesen?

Gruss
Dieter

Delphi-Lover 25. Apr 2005 15:00

Re: Erweiterte Dateiinformationen schreiben
 
Hello,

For the community.

I was just fighting to read the filesummaryinfo and nothing seems to work, so I puzzled with some sources from this site and the main source from Serge Perevoznyk (that you can find on 10.000 sites via Google) Here is the source that will work on WindowsXP and Delphi >= 5.

Code:
unit UFileSummaryInfo;

interface

uses Windows, ActiveX, ComObj;

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.

//I've changed the numbers of the original constants to get the right PID...
//e.g. I decreased all const with 2...
// Summary Information
 PID_TITLE       = 0; //2;
 PID_SUBJECT     = 1;
 PID_AUTHOR      = 2;
 PID_KEYWORDS    = 3;
 PID_COMMENTS    = 4;
 PID_TEMPLATE    = 5;
 PID_LASTAUTHOR  = 6;
 PID_REVNUMBER   = 7;
 PID_EDITTIME    = 8;
 PID_LASTPRINTED = 9;
 PID_CREATE_DTM  = 10;
 PID_LASTSAVE_DTM = 11;
 PID_PAGECOUNT   = 12;
 PID_WORDCOUNT   = 13;
 PID_CHARCOUNT   = 14;
 PID_THUMBNAIL   = 15;
 PID_APPNAME     = 16;
 PID_SECURITY    = 17;

 // 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 : string) : boolean;
function GetFileSummaryInfo(const FileName: WideString;
 GUID_SummaryType: TGUID; PID_InfoType: cardinal): WideString;

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 :             //Address of an interface pointer
 IStorage ) : HResult; stdcall; external 'ole32.dll';

implementation

uses SysUtils;

function PropertyPIDToCaption(const ePID: Cardinal): string;
begin
 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;

function IsNTFS(AFileName : string) : boolean;
var fso, drv : OleVariant;
begin
  IsNTFS := False;
  fso := CreateOleObject('Scripting.FileSystemObject');
  drv := fso.GetDrive(fso.GetDriveName(AFileName));
  if drv.FileSystem = 'NTFS' then IsNTFS := True;
end;

function GetFileSummaryInfo(const FileName: WideString;
  GUID_SummaryType: TGUID; PID_InfoType: cardinal): WideString;
var
  Stg: IStorage;
  PropSetStg: IPropertySetStorage;
  PropStg: IPropertyStorage;
  PropVariant: array of TPropVariant;
  PropEnum: IEnumSTATPROPSTG;
  PropStat: STATPROPSTG;
  PIDCount: Integer;
  HRslt: HResult;
  PropSpec: array of TPropSpec;
begin
  Result:='';

  //I use the STGFMT_ANY const instead of the original STGFMT_FILE that didn't work at all...
  if(StgOpenStorageEx(pwidechar(FileName),STGM_READ or STGM_SHARE_DENY_WRITE,
    STGFMT_ANY,0,nil,nil,@IID_IPropertySetStorage,Stg) = S_OK) then
  begin
    PropSetStg := Stg as IPropertySetStorage;

    If (PropSetStg.Open(GUID_SummaryType,
                        STGM_READ or STGM_SHARE_EXCLUSIVE,
                        PropStg)) = S_OK Then
    Begin
      If PropStg.Enum(PropEnum) = S_OK then
      Begin
        PIDCount := 0;

        HRslt := PropEnum.Next(1, PropStat, nil);
        while HRslt = S_OK do
        begin
          inc(PIDCount);
          SetLength(PropSpec,PIDCount);
          PropSpec[PIDCount-1].ulKind := PRSPEC_PROPID;
          PropSpec[PIDCount-1].propid := PropStat.propid;
          HRslt := PropEnum.Next(1, PropStat, nil);
        end;

        SetLength(PropVariant,PIDCount);
        HRslt := PropStg.ReadMultiple(PIDCount, @PropSpec[0], @PropVariant[0]);

        // Use this to get the Incomming PID_InfoType
        if HRslt <> S_FALSE then
        Begin
          if PropVariant[PID_InfoType].vt = VT_LPSTR then
            if Assigned(PropVariant[PID_InfoType].pszVal) then
              Result := PropVariant[PID_InfoType].pszVal;
        End;

        //Use This to loop through all PID's
        //(and ignore the incomming PID_InfoType)
        {
        for k := 0 to PIDCount -1 do
        begin
          S := '';
          if PropVariant[k].vt = VT_LPSTR then
            if Assigned(PropVariant[k].pszVal) then
              S := PropVariant[k].pszVal;

          S := Format(IntToStr(k)+' '+PropertyPIDToCaption(PropSpec[k].Propid)+ ' %s',[s]);
          if S <> '' then Result := Result + S + #13;
        end;
        }
      End;
    End;
  end;
end;

end.
Use the function in this way:

Code:
Var FileName,Info : WideString;
Begin
  If IsNTFS(FileName) Then
  Begin
    Info:=GetFileSummaryInfo(FileName,FmtID_SummaryInformation,PID_KEYWORDS);
    MessageDlg(Info,mtCustom,[mbOK], 0);
  End;
End
Experiment with the PID_ numbers and these values

FmtID_SummaryInformation
FMTID_DocSummaryInformation
FMTID_UserDefinedProperties

Greets,

Delphi-Lover

MathiasSimmack 25. Apr 2005 16:19

Re: Erweiterte Dateiinformationen schreiben
 
Zitat:

Zitat von Delphi-Lover
I was just fighting to read the filesummaryinfo and nothing seems to work, so I puzzled with some sources from this site and the main source from Serge Perevoznyk (that you can find on 10.000 sites via Google) Here is the source that will work on WindowsXP and Delphi >= 5.

You just renamed a few variables, huh? :stupid: I'm sorry, but I don't see a difference between your version and the one, Luckie spoke about in his first post. To be honest: I didn't like the original unit. So, I made another version, you can get here. I mean, I can set values
Zitat:

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  mal : TMultipleArrayList;
begin
  SetLength(mal,3);
  mal[0].pidInfoType := PIDSI_AUTHOR;
  mal[0].pidInfoStr := 'Me, the one and only';
  mal[1].pidInfoType := PIDSI_COMMENTS;
  mal[1].pidInfoStr := 'Tralala';
  mal[2].pidInfoType := PIDSI_TITLE;
  mal[2].pidInfoStr := 'My title';

  SetMultipleFileSummaryInfo('MeineDatei.txt',
    FMTID_SummaryInformation,
    mal);

  SetLength(mal,0);
end;

you just read them. ;)

BlueStarHH 22. Jul 2009 20:35

Re: Erweiterte Dateiinformationen schreiben
 
Ich nutze jetzt folgenden Code um die FileSummaryInfos unter Delphi 2009 auszulesen. Wie kann ich das unicode-kompatibel machen? Wenn in den FileSummaryInfos Unicode gespeicher ist kommen mit dieser Funktion nur kryptische falsche Zeichen zurück:

Delphi-Quellcode:
function GetFileSummaryInfo(const FileName: WideString;
  GUID_SummaryType: TGUID; PID_InfoType: cardinal): string;
var
  hr         : HRESULT;
  Stg        : IStorage;
  PropSetStg : IPropertySetStorage;
  PropStg    : IPropertyStorage;
  PropSpec   : TPropSpec;
  PropVariant : TPropVariant;
begin
  Result     := '';
  hr         := S_FALSE;


  // for 9x and NT4 users
  if(not Win2k) or (@StgOpenStorageEx = nil) then
    hr := StgOpenStorage(pwidechar(FileName),nil,STGM_READ or
      STGM_SHARE_DENY_WRITE,nil,0,Stg)
  // for 2000, XP and higher
  else if(@StgOpenStorageEx <> nil) then
    hr := StgOpenStorageEx(pwidechar(FileName),STGM_READ or
      STGM_SHARE_DENY_WRITE,STGFMT_ANY,0,nil,nil,
      @IID_IPropertySetStorage,Stg);


  if(hr = S_OK) then
  begin
    PropSetStg := Stg as IPropertySetStorage;

    if(PropSetStg.Open(GUID_SummaryType,STGM_READ or
      STGM_SHARE_EXCLUSIVE,PropStg) = S_OK) then
    begin
      PropSpec.ulKind := PRSPEC_PROPID;
      PropSpec.propid := PID_InfoType;


      if(PropStg.ReadMultiple(1,@PropSpec,@PropVariant) = S_OK) and
        (PropVariant.vt = VT_LPSTR) and
        (PropVariant.pszVal <> nil) then
      Result := PropVariant.pszVal;
    end;
  end;
end;

HPW 1. Mär 2010 07:27

Re: Erweiterte Dateiinformationen schreiben
 
Hallo,

Ich benutzte SetFileSummaryInfo und GetFileSummaryInfo auf XP-Rechnern ohne Probleme.
Der Explorer und auch der TotalCommander (mit Plugin ShellDetails) zeigen die Infos problemlos an.

Wenn ich das nun aber auf einem Vista-Laptop mache, bekomme ich die Felder nicht im Explorer angezeigt.
Auch der TotalCommander zeigt nichts mehr an. WIN 7 konnte ich nicht testen.

Es gibt zwar sehr viel mehr Feld-Namen, aber die alten Einträge werden nicht gefunden.
Sie sind aber vorhanden da ich die Einträge mit meiner GetFileSummaryInfo immer noch lesen kann.

Hat MS da wieder was geändert?


Grüsse,

Hans-Peter

HPW 2. Mär 2010 19:36

Re: Erweiterte Dateiinformationen schreiben
 
Nach weiterer Recherche habe ich herausgefunden, das MS das gravierend geändert hat.
Die Metadaten sind jetzt Bestandteil des File-Formats und nicht mehr im separaten NTFS-Stream.
So kann man bei MS lesen das eine TXT oder RTF-Datei keine Dateiinfo mehr habe kann,
da deren Dateiformat das eben nicht vorsieht.

Nun ist das von MS mal wieder schlecht gemacht.
Eine inkompatible Umstellung des Explorers.
Man hat in der XP-Welt tausende Dateien mit Infos im NTFS-Stream angelegt und bekommt Sie nach einem
Windows-Upgrade einfach nicht mehr zu sehen. Super.

Da bleibt nur einen Custom-Browser zu schreiben der die Info wieder mit anzeigt.

Grüsse,

Hans-Peter

Luckie 2. Mär 2010 21:08

Re: Erweiterte Dateiinformationen schreiben
 
Nein wieso? Du kannst die Infos doch immer noch per ADS schreiben. Oder muss ich dich so verstehen, dass der Explorer die ADS Daten auch nicht mehr anzeigt?

HPW 2. Mär 2010 21:34

Re: Erweiterte Dateiinformationen schreiben
 
Zitat:

Oder muss ich dich so verstehen, dass der Explorer die ADS Daten auch nicht mehr anzeigt?
Genau das ist das Problem.
Unter Vista sieht man keine NTFS-Infos mehr.
Auch mit RechtsKlick/Eigenschaften wird nichts mehr davon angezeigt.

Mit meinem eigenen Programm kann ich die Infoes nach wie vor schreiben und lesen.

Grüsse,

Hans-Peter

norwegen60 15. Mai 2015 10:47

AW: Erweiterte Dateiinformationen schreiben
 
Hallo,

das Thema begleitet mich auch seit Jahren und ich hatte mit den NTFS-Streams eine halbwegs brauchbare Lösung da ich die Dateiinformationen mit dem eigenen Programm ausgelesen habe.

Jetzt wird aber die Nachfrage größer, dass die Dateiinformationen auch über den Explorer oder per SharePoint gelesen werden sollen. Und da dabei auch Dateiformate gehandelt werden, die die Dateiinformationen nicht schon im Dateiformat haben, möchte ich doch mal nachfragen, ob nicht auch Microsoft erkannt hat, dass die NTFS-Lösung nicht die schlechteste war.

Wir handeln in der Firma eben auch einige TXT-Dateien und HEX-Dateien, z.B. für Firmwaren und da war es sehr hilfreich, gewisse Informationen auch im ASCII-Format ablegen zu können (Gerät, Version, ...)

Hat jemand eine Idee wie man das lösen kann.

Grüße
Gerd

Dalai 15. Mai 2015 10:58

AW: Erweiterte Dateiinformationen schreiben
 
Zitat:

Zitat von norwegen60 (Beitrag 1301635)
Hat jemand eine Idee wie man das lösen kann.

Es gibt zwar eine Reihe von Möglichkeiten, aber wenn die Bedingung gestellt ist, dass es auch im Explorer funktionieren soll, wird es sehr schnell sehr dünn - das ist eben kein Dateimanager sondern nur ein Dateibrowser (ich nenne ihn auch gern Spielzeug).

Wie wär's mit den steinalten Formaten file_id.diz oder descript.ion und ähnlichen? Keine Ahnung, ob der Explorer was damit anfangen kann, aber versuchen kann man es ja mal.

MfG Dalai

mkinzler 15. Mai 2015 11:00

AW: Erweiterte Dateiinformationen schreiben
 
Für die Streams müsstest Du Dir eine Erweiterung für den Explorer schreiben. Denn der standard Windows-Explorer zeigt diese nicht an.
Das ist auch der Grund warum die Streams in Verruf geraten sind. Den man konnte so Malware und Co. gut verstecken.

norwegen60 17. Mai 2015 15:19

AW: Erweiterte Dateiinformationen schreiben
 
Das Problem ist, dass es auf den Rechner unterschiedlichster Anwender funktionierne soll. Und da ist es unmöglich, Zusatzsoftware zu installieren. Es ist schon nicht einfach, ihnen zu erklären, dass sie im Explorer die Spalte "Titel" aktivieren sollen.

Dann wird mir wohl weiterhin nichts anderes übrig bleiben als solche Infos im Dateinamen anzugeben. Auf dass er schön lang werde.

Danke trotzdem
Gerd


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:50 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz