Zum Abschluss werfen wir noch einen Blick auf einen Block mit Versionsinformationen. Sie kennen diese Angaben, wenn Sie sich die Eigenschaften einer Datei ansehen. Enthalten ist meist das Copyright, der Firmenname, der interne Name, usw.
Eine Versionsresource trägt den Bezeichner VERSIONINFO und sieht wie folgt aus
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,0
PRODUCTVERSION 1,0,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x10004L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040704b0"
BEGIN
VALUE "CompanyName", "Win32-API-Tutorials\0"
VALUE "FileDescription", "Beschreibung\0"
VALUE "FileVersion", "1.0.0.0\0"
VALUE "InternalName", "Interner Name\0"
VALUE "LegalCopyright", "Copyright © 2004 Mathias Simmack.\0"
VALUE "OriginalFilename", "Originaler Dateiname\0"
VALUE "ProductName", "Produkt\0"
VALUE "ProductVersion", "1,0,0,0\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x407, 1200
END
END
Die Angaben für FILEFLAGS, FILEOS, FILETYPE und FILESUBTYPE richten sich nach bestimmten Werten, die Sie in der Headerdatei "WinVer.h" des PSDK finden können.
So sehen Sie bspw., dass die Angabe FILEFLAGS im Normalfall Null ist. Wenn die Compilerdirektive _DEBUG gesetzt wird, dann erhält das Attribut den Wert Eins (VS_FF_DEBUG): das Debug-Flag wird gesetzt. Ähnlich verhält es sich auch mit den anderen Angaben. Der Wert $10004 in FILEOS steht z.B. für eine Win32-Anwendung.
Aufmerksam machen möchte ich Sie auf die beiden Angaben
FILEVERSION 1,0,0,0 PRODUCTVERSION 1,0,0,0
Das Komma ist kein Fehler, weil es sich bei diesen beiden Angaben um 32-Bit-Werte handelt. Jede Stelle ist also eins der vier Bytes. Im Gegensatz dazu handelt es sich hier um Strings:
VALUE "FileVersion", "1.0.0.0\0"
VALUE "ProductVersion", "1,0,0,0\0"
Es wäre also in dem Fall unerheblich, ob Sie zur Trennung der einzelnen Stellen einen Punkt oder ein Komma verwenden. Nur dürfen Sie bei all diesen Strings (s. auch oben) nicht die abschließende Null vergessen, die ich in dem Auszug fett markiert dargestellt habe.
Eine Regel gibt es für diesen Stringblock in dem Sinn eigentlich nicht. Die oben gezeigten Werte sind üblich, Sie könnten aber auch eigene Strings definieren, die dann natürlich auch angezeigt werden:
VALUE "Grüße gehen an", "...\0"

Zum Auslesen von Versioninformationen benötigen wir ein paar Funktionen aus dem API. Zum einen müssen wir herausfinden, wie groß diese Ressource ist. Dazu benutzen wir "GetFileVersionInfoSize", die den entsprechenden Wert zurückgibt.
Wenn der Wert größer als Null ist, reservieren wir entsprechend Speicher und laden den Versionsblock mit der Funktion "GetFileVersionInfo"
vis := GetFileVersionInfoSize(pchar(FileName),dummy); if(vis > 0) then begin GetMem(vi,vis); try GetFileVersionInfo(pchar(Filename),0,vis,vi); if(vi = nil) then exit;
Wie Sie in der o.g. Beispielressource sehen können, befinden sich die gesuchten Strings in einem Block namens "StringFileInfo". Darunter befindet sich ein weiterer Block namens "040704b0", dessen Name allerdings variieren kann. Aus dem Grund muss vorher der zweite Block, "VarFileInfo", ausgelesen werden, dessen "Translation"-Wert mit dem o.g. Block identisch ist. Der "Translation"-Wert ist ein longint, dessen zwei Stellen (word) Sie im Beispiel sehen können.
Um den Wert auszulesen, verwenden wir "VerQueryValue", die zum einen den Zeiger auf den Speicher benötigt, gefolgt von der Angabe des gewünschten Blocks, dem Zielzeiger als dritten Parameter und einer Variablen für die Größe ganz am Schluss
VerQueryValue(vi,'\\VarFileInfo\\Translation',translation,vis);
if(translation = nil) then exit;
Der Zeiger "translation" zeigt nun auf den Wert. Um auf den Blocknamen (im Beispiel "040704b0") zu kommen, reicht es aus, den longint hexadezimal formatiert zu verwenden. Der Einfachheit halber kann man dazu "Format" benutzen. Auf die Weise sparen wir auch das Hantieren mit weiteren Variablen, weil wir den gesuchten Stringnamen gleich anhängen:
VerQueryValue(vi,
pchar(Format('\\StringFileInfo\\%.4x%.4x\\%s',
[LOWORD(longint(translation^)),HIWORD(longint(translation^)),
BlockKey])),ip,vis);
if(ip = nil) then exit;
SetString(Result,pchar(ip),vis);
finally
FreeMem(vi);
end;
end;
Das Ergebnis ist die Funktion "GetFileInfo", die Sie zum Auslesen aller Stringwerte des "StringFileInfo"-Blocks verwenden können; bspw. für die Beschreibung:
SetDlgItemText(hDlg,CID_LABEL1, pchar(GetFileInfo(paramstr(0),'FileDescription')));
und die auch im Beispielprogramm benutzt wird.
Dabei handelt es sich um die Werte am Anfang,
FILEVERSION 1,0,0,0 PRODUCTVERSION 1,0,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x10004L FILETYPE 0x1L FILESUBTYPE 0x0L
Diese müssen separat abgefragt werden, was allerdings auch etwas einfacher wird, da es dafür ein spezielles Record mit allen Daten gibt, auf die man einfacher zugreifen kann. Die Grundfunktion sieht auch ähnlich aus, nur dass diesmal die beiden Blöcke keine Rolle spielen, sondern dass wir nach dem Reservieren des Speichers und dem Laden der Versionsressource auf den Hauptblock zugreifen
VerQueryValue(vi,'\\',pointer(FixBuf),dummy);
if(FixBuf = nil) then exit;
"FixBuf" ist ein Zeiger auf ein Record vom Typ
Result := Format(FormatStr,
[(FixBuf^.dwFileVersionMS and $FFFF0000) shr 16,
FixBuf^.dwFileVersionMS and $0000FFFF,
(FixBuf^.dwFileVersionLS and $FFFF0000) shr 16,
FixBuf^.dwFileVersionLS and $0000FFFF]);