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 StringFileInfo/VarFileInfo für Dateiversion schreiben (https://www.delphipraxis.net/134531-stringfileinfo-varfileinfo-fuer-dateiversion-schreiben.html)

Mazel 24. Mai 2009 08:15


StringFileInfo/VarFileInfo für Dateiversion schreiben
 
Liste der Anhänge anzeigen (Anzahl: 2)
Guten Morgen,

nachdem es mir mit der Hilfe von Zacherl gelungen ist, die Versionsinformation mittels UpdateResource zu einer Datei hinzuzufügen, stehe ich nun vor dem Problem die StringFileInfo zu setzen bzw. zu schreiben. Auf den msdn Seiten sind die nötigen Strukturen (StringFileInfo, VS_VERSIONINFO) gut beschrieben, jedoch ist mir nicht klar, wie ich der einen Struktur - VS_VERSIONINFO - die Struktur der StringFileInfo übergebe, da es eigentlich ein Word Array sein müsste.
Genauso muss auch die VarFileInfo geschrieben werden.
Ohne diese beiden Informationen wird der Reiter unter Windows nicht richtig dargestellt. (siehe Anhang)

Hat jemand dies schonmal getan, wenn ja, mit welcher Technik. Ich habe bei meinen Recherchen einen Ansatz gefunden, der Streams verwendetet, leider ist mir die URL abhanden gekommen.

Würde mich auf Antworten freuen.

Gruß
Mazel

sx2008 25. Mai 2009 05:00

Re: StringFileInfo/VarFileInfo für Dateiversion schreiben
 
Die Strukturen werden einfach hintereinander "geklebt".
Man kann keine normalen Pascal Records verwenden, sondern man braucht einen Speicherblock (mit GetMem) und baut dann dort die Strukturen auf.
Die Herausforderung ist dann, dass wLength von VS_VERSIONINFO auch alle weiteren StringFileInfo Blöcke berücksichtigen muss.

Mazel 25. Mai 2009 18:16

Re: StringFileInfo/VarFileInfo für Dateiversion schreiben
 
Hallo und danke für deine Antwort.

Mit GetMem arbeiten, ok. Das heißt in dem Fall für jeden Block den ich hizufüge oder von vorn herein soviel reservieren, wie cih für alles benötige, was zur Folge habe muss, dass ich vorher die Größe brauch.

Hast du eventuell ein Beispiel?

Ich habe mir gestern eine Variante überlegt, die funktioniert aber nicht "das Wahre" ist. Ich habe aus ResHacker eine binäre Datei erstellt die eine vollständige VERSIONINFO Struktur mit allen unterstrukturen enthält. Diese lade über ein FileStream in mein Programm und übergebe diese dann per UpdateResource an die jeweilige Anwendung. Nachteil ist aber dabei, dass ich danach wieder die Werte ändern muss und so wieder vor dem Problem stehe. Hier mal der Code dazu, eventuell kann man mir daraus auch einen weiteren Tipp geben.
Delphi-Quellcode:
{--[AddResourceStruture]-------------------------------------------------------}

function AddResourceStruture(ExecutableFile: String): Boolean;
var
 FileStream    : TFileStream;
 ResourceHandle : THandle;
 DataLength    : Cardinal;
 Data          : Pointer;
 Path          : String;

begin
 Result := False;
 Path := ExtractFilePath(ParamStr(0));
 if not EndsWith(Path, '\') then Path := Path + '\';
 Path := Path + 'Resource\VSResource.bin';
 ResourceHandle := BeginUpdateResource(PChar(ExecutableFile), False);
 if (ResourceHandle <> 0) then
  begin
   FileStream := TFileStream.Create(Path, fmOpenRead);
   FileStream.Seek(0, soFromBeginning);
   DataLength := FileStream.Size;
   try
    GetMem(Data, DataLength);
    FileStream.Read(Data^, DataLength);
    FileStream.Free;
    Result := True;
    // for german language only - 1031
    if not UpdateResource(ResourceHandle, RT_VERSION, MakeIntResource(1), 1031, Data, DataLength) then Result := False;
    if not EndUpdateResource(ResourceHandle, False) then Result := False;
   finally
    FreeMem(Data);
   end;
   ShowMessage(SysErrorMessage(GetLastError));
  end;
end;
Gruß
Mazel

sx2008 26. Mai 2009 05:10

Re: StringFileInfo/VarFileInfo für Dateiversion schreiben
 
Zitat:

Zitat von Mazel
Das heißt in dem Fall für jeden Block den ich hizufüge oder von vorn herein soviel reservieren, wie cih für alles benötige, was zur Folge habe muss, dass ich vorher die Größe brauch.

Genau das ist das Problem, was die Sache so knifflig macht.
Eine Möglichkeit wäre, mit den innersten String Structure zu beginnen und diese dann zu einer StringTable Structure zusammenzubauen, die dann wiederum zur StringFileInfo Structure aufgebaut wird.

Hier völlig ungetestet als Anregung:
Delphi-Quellcode:
function BuildStringStructure(Key, Value : Widestring):Ansistring;
var
  p : PInteger;
begin
   key := key + #0; // Terminierung mit 0x0000
   if odd(length(key)) then
      key := key + #0; // auf 32-bit boundary bringen

   SetLength(result, 2 + 2 + 2+ 2*Length(key) + 2 * Length(Value))
   p := PInteger(PChar(result));
   p^ := length(Result); // WORD  wLength befüllen
   Inc(p);
   p^ := length(Value) * 2; // WORD  wValueLength befüllen
   Inc(p);
   p^ := 1; // WORD  wType
   Inc(p);
   // jetzt muss noch Key und Value in den Resultstring kopiert werden (mit Move())
   // ich spar mir das mal
end;
Damit kann man jetzt mehrere String Structures zusammen verketten:
Delphi-Quellcode:
data := BuildStringStructure('Comments', 'ein Test') + BuildStringStructure('CompanyName', 'DP') + ....
Jetzt muss das zu einer StringTable Structure aufgebaut werden:
Delphi-Quellcode:
function BuildStringTableStructure(Key:WideString; const Children:Ansistring):AnsiString;
Wenn man soweit ist, ist der Aufbau der StringFileInfo Structure relativ einfach:
Delphi-Quellcode:
function BuildStringFileInfoStructure(Key:WideString; const Children:Ansistring):AnsiString;
begin
   Result := BuildStringTableStructure('StringFileInfo', Children);
end;

Mazel 26. Mai 2009 19:10

Re: StringFileInfo/VarFileInfo für Dateiversion schreiben
 
Guten Abend,

danke für diese schönen Tipps. Ich habe jetzt schonmal versucht etwas zu kreieren, bin aber bis jetzt noch zu keinem Erfolg gekommen und zum zeigen muss ich noch einiges zusammenfassen und weiter ausprobieren. :roll:
Es ist eben nicht einfach diese Struktur zusammen zubauen :)

Wenn ich dein Code als Grundlage nehme verarbeite ich am Ende den AnsiChar. Ich bräuchte ja aber den Pointer, damit ich diesen an Children weitergeben kann.

Btw.: ich hatte von Zacherl folgende Strukturen bekommen, die ich ein wenig aufgebessert habe, damit es funktioniert.
Delphi-Quellcode:
type
  TFIXEDFILEINFO = packed record
    dwSignature:       DWord;
    dwStrucVersion:    DWord;
    dwFileVersionMS:   DWord;
    dwFileVersionLS:   DWord;
    dwProductVersionMS: DWord;
    dwProductVersionLS: DWord;
    dwFileFlagsMask:   DWord;
    dwFileFlags:       DWord;
    dwFileOS:          DWord;
    dwFileType:        DWord;
    dwFileSubtype:     DWord;
    dwFileDateMS:      DWord;
    dwFileDateLS:      DWord;
  end;

     TVERSIONSTRING = packed record
    wLength:           Word;
    wValueLength:      Word;
    wType:             Word;
    {$IFDEF D12ORABOVE}
    szKey:             String;
    {$ELSE}
    szKey:             WideString;
    {$ENDIF}
    Padding:          TWordArray;
    {$IFDEF D12ORABOVE}
    Value:             String;
    {$ELSE}
    Value:             WideString;
    {$ENDIF}
  end;

  TVERSIONSTRINGTABLE = packed record
    wLength:           Word;
    wValueLength:      Word;
    wType:             Word;
    {$IFDEF D12ORABOVE}
    szKey:             Array[0..8] of Char;
    {$ELSE}
    szKey:             Array[0..8] of WideChar;
    {$ENDIF}
    Padding:           TWordArray;
    Children:          array of TVERSIONSTRING;
  end;
  TSTRINGFILEINFO = packed record
    wLength:           Word;
    wValueLength:      Word;
    wType:             Word;
    {$IFDEF D12ORABOVE}
    szKey:             Array[0..14] of Char;
    {$ELSE}
    szKey:             Array[0..14] of WideChar;
    {$ENDIF}
    Padding:           TWordArray;
    Children:          Array of TVERSIONSTRINGTABLE;
  end;

  TVERSIONINFO = packed record
    wLength:           Word;
    wValueLength:      Word;
    wType:             Word;
    szKey:             Array[0..15] of WideChar;
    Padding1:          Word;
    Value:             TFIXEDFILEINFO;
    Padding2:          Word;
    Children:          Array of TSTRINGFILEINFO;
  end;
Anfangs gab es Probleme in der Reihenfolge, nachdem ich das geändert hatte konnte ich die VERSIONINFO schreiben, aber die children gehen bis heute noch nicht, da tu ich mich wohl etwas schwer.

Gruß
Mazel

Zacherl 27. Mai 2009 07:30

Re: StringFileInfo/VarFileInfo für Dateiversion schreiben
 
Ich hatte es damals so gemacht, dass ich zuerst die Strukturen aufgebaut habe, wobei ich die Größe erst einmal komplett umberechnet gelassen habe.
In einem zweiten Durchgang habe ich dann für jeden Zweig jeweils von innen nach außen die Größe berechnet. Bei dieser Struktur ist dies ja gar nicht mal so schwer, da die Anzahl der Zweige statisch gestaltet ist.

sx2008 27. Mai 2009 08:53

Re: StringFileInfo/VarFileInfo für Dateiversion schreiben
 
Zitat:

Zitat von Mazel
Wenn ich dein Code als Grundlage nehme verarbeite ich am Ende den AnsiChar. Ich bräuchte ja aber den Pointer, damit ich diesen an Children weitergeben kann.

Nein, man kann doch AnsiStrings wunderbar mit dem pluszeichen miteinander verketten.
Dabei werden die beteiligten Stringstücke in einen neuen String kopiert.
Mit der Technik von Zacherl könnte man sich die kopierei sparen, aber so ist es viel eleganter.

alles ungetestet:
Delphi-Quellcode:
function BuildStringStructure(Key, Value : Widestring):Ansistring;
var
  p : PInteger;
begin
   key := key + #0; // Terminierung mit 0x0000
   if odd(length(key)) then
      key := key + #0; // auf 32-bit boundary bringen

   SetLength(result, 2 + 2 + 2+ 2*Length(key) + 2 * Length(Value))
   p := PInteger(PChar(result));
   p^ := length(Result); // WORD  wLength befüllen
   Inc(p);
   p^ := length(Value) * 2; // WORD  wValueLength befüllen
   Inc(p);
   p^ := 1; // WORD  wType
   Inc(p);

   // Key und Value auf den Ergebnisstring kopieren
   Move(key[1], p^, length(key)*2);
   Inc(p, length(key)*2);
   Move(Value, p^, length(value)*2);
   // hoffentlich war die Längenberechnung von result richtig ;-)
end;

function BuildStringTableStructure(Key:WideString; const Children:Ansistring):AnsiString;
var
  p : PInteger;
begin
   key := key + #0; // Terminierung mit 0x0000
   if odd(length(key)) then
      key := key + #0; // auf 32-bit boundary bringen

   SetLength(result, 2 + 2 + 2+ 2*Length(key)); // wird später noch länger...
   p := PInteger(PChar(result));
   p^ := length(Result) + length(Children); // WORD  wLength befüllen
   Inc(p);
   p^ := length(Children) * 2; // WORD  wValueLength befüllen
   Inc(p);
   p^ := 1; // WORD  wType
   Inc(p);
   Move(key[1], p^, length(key)*2);

   // einfach den übergebenen Children-Datenblock hinten anhängen
   Result := Result + Children;
end;

function BuildStringFileInfoStructure(const Children:Ansistring):AnsiString;
begin
   Result := BuildStringTableStructure('StringFileInfo', Children);
end;

function BuildVarSructure(Value : Widestring):Ansistring;
begin
  Result := BuildStringStructure('Translation', Value);
end;
und dann die Daten quasi von Innen nach Aussen aufbauen:
Delphi-Quellcode:
data := BuildStringStructure('Comments', 'ein Test') + BuildStringStructure('CompanyName', 'DP') + ....;
data := BuildStringTableStructure(LANG_CODE_8BYTES, data);
data := BuildStringFileInfoStructure(data);
Jetzt fehlt nur noch die VS_VERSIONINFO Structure:
Delphi-Quellcode:
function BuildVersionInfoStructure(const fixedinfo:TFIXEDFILEINFO;const Children:Ansistring):AnsiString;

rakekniven 14. Jul 2009 15:07

Re: StringFileInfo/VarFileInfo für Dateiversion schreiben
 
Hallo,

in dem Zusammhang habe ich eine Frage zur LCID.

In ganz vielen Beiträgen im Internet wird ja beschrieben wie man seine Versionsinformationen in einer Textdatei zu pflegen.

Wir haben sie "Version.RC" genannt und folgenden Block drin

BLOCK "VarFileInfo"
{
VALUE "Translation", 1031, 1252
}

Viele der genannten Seiten haben anstattt "1031" den Wert "1033" drin.
Warum?

Bei einigen Projekten hier war auch bei uns "1033" drin.
Windows schluckt das und zeigt im Windows-Explorer die Infos richtig an.

Heute haben wir eine Funktion gesucht zum Auslesen der Versionsinformationen und sind dabei über http://www.delphipraxis.net/internal...t.php?t=159069 gestolpert.

Der Code funktioniert mit "1031, 1252" sehr gut, mit "1033, 1252" liefert er Schrott zurück.
Ich vermute dass der Grund ist, dass die LCID nicht zur Codepage passt.

Weiss hier jemand mehr?

Gruß


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:07 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