Die Entwicklung von Software für das Windows-Ökosystem ist eine komplexe Aufgabe, bei der Zuverlässigkeit und Debugbarkeit an vorderster Stelle stehen. Ein entscheidender Faktor hierfür ist ein robustes Logging-System. Doch was gehört alles in ein gutes Log? Neben Zeitstempeln, Fehlercodes und Stapelverfolgungen ist die genaue Kenntnis der Betriebssystemumgebung, insbesondere der Windows-Versionsnummer, unerlässlich. Sie ermöglicht es Entwicklern und Support-Teams, Probleme präziser zu reproduzieren, Kompatibilitätsfragen zu klären und gezielte Lösungen anzubieten.
Leider ist die Ermittlung der *tatsächlichen* Windows-Versionsnummer weitaus trickreicher, als man auf den ersten Blick annehmen könnte. Historisch bedingte Kompatibilitätsmechanismen können dazu führen, dass Ihre Anwendung eine falsche oder irreführende Versionsnummer meldet. In diesem umfassenden Artikel tauchen wir tief in die Materie ein und zeigen Ihnen, wie Sie mithilfe der C-API die korrekte Windows-Versionsnummer zuverlässig ermitteln und in Ihren Log-Dateien festhalten können.
### Warum die Windows-Versionsnummer so wichtig ist
Stellen Sie sich vor, ein Benutzer meldet einen Absturz Ihrer Anwendung. Ohne die genaue Betriebssystemversion ist es schwierig zu beurteilen, ob der Fehler versionsspezifisch ist, vielleicht durch ein bestimmtes Update behoben wurde oder sogar auf eine Inkompatibilität mit einer älteren Windows-Edition zurückzuführen ist. Hier sind einige Gründe, warum diese Information so kritisch ist:
1. **Gezieltes Debugging**: Viele API-Verhaltensweisen, Systemkomponenten und Sicherheitsrichtlinien ändern sich zwischen Windows-Versionen. Ein Fehler, der auf Windows 10 auftritt, kann auf Windows 11 völlig anders (oder gar nicht) erscheinen. Die Versionsnummer grenzt die mögliche Fehlerursache erheblich ein.
2. **Kompatibilitätsprüfung**: Wenn Ihre Software bestimmte Systemanforderungen hat, können Sie mithilfe der geloggten Version sofort feststellen, ob die Umgebung des Benutzers diese erfüllt.
3. **Support und Reproduzierbarkeit**: Support-Mitarbeiter benötigen diese Daten, um eine Umgebung nachzustellen oder bekannte Probleme schneller zu identifizieren. Ohne sie ist die Problembehandlung ein Ratespiel.
4. **Feature-Verfügbarkeit**: Bestimmte Windows-Funktionen oder APIs sind erst ab einer bestimmten Version verfügbar. Das Wissen um die Version hilft zu verstehen, warum bestimmte Features bei einem Benutzer nicht funktionieren.
5. **Analyse von Nutzungsstatistiken**: Für Produktmanager und Entwickler kann die Verteilung der Windows-Versionen unter den Benutzern wertvolle Einblicke in die Zielgruppe und zukünftige Entwicklungsstrategien liefern.
Kurz gesagt: Die Windows-Versionsnummer ist ein essenzieller Bestandteil einer jeden aussagekräftigen Log-Datei.
### Die Fallen der Vergangenheit: `GetVersionEx` und Anwendungsmuster
Jeder Windows-Entwickler, der schon länger im Geschäft ist, kennt wahrscheinlich die Funktion `GetVersionEx`. Sie war über viele Jahre hinweg die Standardmethode zur Abfrage der Betriebssystemversion. Doch mit Windows 8.1 und den darauf folgenden Versionen wurde sie faktisch deprecated und liefert standardmäßig nicht mehr die *tatsächliche* Versionsnummer des installierten Betriebssystems, sondern eine ältere Kompatibilitätsversion (z.B. Windows 8).
Warum das so ist, liegt in der Natur der Windows-Abwärtskompatibilität: Viele ältere Anwendungen wurden hart gegen spezifische Windows-Versionen kompiliert oder erwarteten bestimmte Versionsnummern, um korrekt zu funktionieren. Um zu verhindern, dass diese Anwendungen auf neueren Windows-Versionen versagen (weil sie dachten, sie liefen auf einem unbekannten OS), führte Microsoft einen Kompatibilitätsmechanismus ein. Wenn eine Anwendung die `GetVersionEx`-Funktion ohne ein spezielles Anwendungsmanifest aufruft, meldet das System eine ältere Version, um sicherzustellen, dass die Anwendung weiterhin funktioniert.
Ein Anwendungsmanifest ist eine XML-Datei, die in die ausführbare Datei Ihrer Anwendung eingebettet wird und wichtige Informationen über die Anwendung selbst enthält. Dazu gehören unter anderem Kompatibilitätseinstellungen. Erst wenn Ihre Anwendung in ihrem Manifest explizit deklariert, welche Windows-Versionen sie unterstützt (z.B. `
**Beispiel eines Manifest-Eintrags für Windows 10/11-Kompatibilität:**
„`xml
„`
Auch mit dem korrekten Manifest ist `GetVersionEx` jedoch nicht die erste Wahl für eine präzise Versionsermittlung, da es immer noch Einschränkungen und subtile Verhaltensweisen gibt. Die Funktion ist primär auf das Abfragen der *Major* und *Minor* Version ausgelegt, weniger auf genaue Build-Nummern oder Editionen, die heute immer wichtiger werden. Für zuverlässiges Logging benötigen wir einen besseren Ansatz.
### Der moderne Ansatz: Die C-API zur Rettung
Glücklicherweise bietet die C-API von Windows robustere und genauere Wege, die benötigten Informationen zu erhalten. Wir konzentrieren uns auf zwei Hauptmethoden: die Undokumentierte, aber weit verbreitete `RtlGetVersion`-Funktion und die offizielle Kombination aus `VerifyVersionInfo` und `GetProductInfo`.
#### Option 1: `RtlGetVersion` (Der „Geheimtipp”)
Die Funktion `RtlGetVersion` ist eine undokumentierte API aus `ntdll.dll`, wird aber intern von Microsoft-Komponenten und vielen Anwendungen verwendet, um die *echte* und unverfälschte Versionsnummer des Betriebssystems abzurufen, unabhängig von Manifesten oder Kompatibilitätsmodi. Trotz ihres undokumentierten Status ist sie überaus stabil und zuverlässig.
Die Verwendung von `RtlGetVersion` ist relativ einfach:
„`c++
#include
#include
// Deklaration der RtlGetVersion Funktion
// Diese Struktur ist im Windows SDK in winnt.h definiert.
// Für RtlGetVersion wird jedoch die OSVERSIONINFOEXW benötigt.
// Wir definieren hier nur die Signatur.
typedef NTSTATUS (WINAPI *RtlGetVersionPtr)(POSVERSIONINFOEXW);
// Eine Hilfsfunktion, um die Windows-Version zu ermitteln
BOOL GetWindowsVersionRtl(OSVERSIONINFOEXW* osvi) {
if (osvi == NULL) {
return FALSE;
}
// Initialisiere die Struktur
ZeroMemory(osvi, sizeof(OSVERSIONINFOEXW));
osvi->dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
// Lade die ntdll.dll
HMODULE hNtdll = LoadLibraryA(„ntdll.dll”);
if (hNtdll == NULL) {
return FALSE;
}
// Hole die Adresse von RtlGetVersion
RtlGetVersionPtr pRtlGetVersion = (RtlGetVersionPtr)GetProcAddress(hNtdll, „RtlGetVersion”);
if (pRtlGetVersion == NULL) {
FreeLibrary(hNtdll);
return FALSE;
}
// Rufe RtlGetVersion auf
NTSTATUS status = pRtlGetVersion(osvi);
// Entlade die ntdll.dll
FreeLibrary(hNtdll);
return NT_SUCCESS(status);
}
// Beispiel der Nutzung im Logging
void LogWindowsVersion() {
OSVERSIONINFOEXW osvi;
if (GetWindowsVersionRtl(&osvi)) {
// Jetzt haben wir die echten Versionsinformationen
// Major: osvi.dwMajorVersion
// Minor: osvi.dwMinorVersion
// Build: osvi.dwBuildNumber
// Revision: osvi.wServicePackMinor (für Windows 10/11 der UBR – Update Build Revision)
// PlatformId: osvi.dwPlatformId
// CSD Version (Service Pack): osvi.szCSDVersion
// … und weitere Felder
// Für Windows 10/11 sind dwMajorVersion=10, dwMinorVersion=0
// Die Build-Nummer ist entscheidend für die genaue Version (z.B. 19041 für 2004, 22000 für Windows 11)
wprintf(L”OS Version: %d.%d.%d (Build %d)n”,
osvi.dwMajorVersion,
osvi.dwMinorVersion,
osvi.dwBuildNumber,
osvi.wServicePackMinor); // Für UBR bei Windows 10/11
wprintf(L”CSD Version: %sn”, osvi.szCSDVersion);
// … Hier würde die Log-Funktion aufgerufen, um diese Daten in die Log-Datei zu schreiben
} else {
wprintf(L”Fehler beim Ermitteln der Windows-Version.n”);
}
}
„`
`RtlGetVersion` füllt die `OSVERSIONINFOEXW`-Struktur mit den korrekten Werten, einschließlich Major-Version, Minor-Version, Build-Nummer und sogar der Service Pack-Information oder der Update Build Revision (UBR) bei neueren Windows-Versionen. Dies ist oft die bevorzugte Methode für Entwickler, die absolute Präzision und Unabhängigkeit vom Anwendungsmakifest wünschen.
#### Option 2: `VerifyVersionInfo` und `GetProductInfo` (Der „Offizielle” Weg)
Wenn Sie eine ausschließlich dokumentierte API-Lösung bevorzugen, ist die Kombination von `VerifyVersionInfo` und `GetProductInfo` der Weg der Wahl. Diese Methode ist zwar etwas aufwendiger, liefert aber ebenfalls sehr detaillierte und genaue Informationen.
1. **`VerifyVersionInfo`**: Diese Funktion prüft, ob die aktuelle Betriebssystemversion bestimmte Kriterien erfüllt. Sie gibt `TRUE` zurück, wenn die Systemversion *größer oder gleich* der von Ihnen angegebenen Version ist. Man kann sie nicht direkt verwenden, um die Version zu *ermitteln*, sondern um sie zu *validieren*. Sie ist jedoch nützlich, um inkrementell die aktuelle Version zu bestimmen, indem man von höheren zu niedrigeren Versionen prüft. Wichtiger ist hier, dass `VerifyVersionInfo` von den Kompatibilitätsmodi des Anwendungsmakifests *nicht* beeinflusst wird – sie liefert immer die tatsächliche Systemversion.
2. **`GetProductInfo`**: Diese Funktion ist entscheidend, um die genaue Windows-Edition zu erhalten (z.B. Home, Pro, Enterprise, Server). Sie liefert eine Produkt-ID, die man dann einer lesbaren Zeichenkette zuordnen kann.
Hier ein Beispiel, wie man diese Funktionen kombiniert:
„`c++
#include
#include
// Helper-Funktion, um eine OSVERSIONINFOEXW Struktur mit der aktuellen Version zu füllen
// Beachte: Diese Funktion *simuliert* das Ermitteln der Version durch sequenzielle Checks
// und ist nicht so direkt wie RtlGetVersion
BOOL GetWindowsVersionInfo(OSVERSIONINFOEX* osVersionInfo) {
ZeroMemory(osVersionInfo, sizeof(OSVERSIONINFOEX));
osVersionInfo->dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
// Initialisiere die Versionsnummern für Windows 10/11
// Beginnen wir mit Windows 10 / 11 (Major 10, Minor 0)
// Build 22000 ist Windows 11
// Build 1904X ist Windows 10
osVersionInfo->dwMajorVersion = 10;
osVersionInfo->dwMinorVersion = 0;
osVersionInfo->dwBuildNumber = 0; // Wird später mit dem tatsächlichen Build überschrieben
// GetVersionEx ist veraltet, aber VerifyVersionInfo verwendet diese Struktur korrekt
// Wir rufen es hier als Fallback auf und nutzen dann GetProductInfo
// WICHTIG: VerifyVersionInfo funktioniert NUR dann, wenn das Manifest aktualisiert wurde!
// Wenn Sie *kein* Manifest verwenden, um Windows 10/11 zu deklarieren, wird VerifyVersionInfo
// trotz seiner scheinbaren Unabhängigkeit von Kompatibilitätsmodi nur die „kompatible” Version liefern.
// Daher ist RtlGetVersion in den meisten Fällen die sicherere Wahl, wenn keine Manifest-Pflege gewünscht ist.
// Wenn Sie es auf die „offizielle” Art machen wollen, müssen Sie einen bekannten Build testen.
// Ein gängiger Ansatz ist es, einen bekannten BUILD zu nutzen und zu prüfen, ob die Version >= ist.
// Da wir die genaue Version wollen, ist GetVersionEx der Startpunkt (mit Manifest) oder RtlGetVersion.
// Da der Benutzer ausdrücklich „korrekte Windows-Versionsnummer” und „C-API” verlangt,
// sollte hier klar sein, dass GetVersionEx (mit Manifest) oder RtlGetVersion die Daten liefert,
// und VerifyVersionInfo nur zum *Vergleichen* nützlich ist.
// Für die *Ermittlung* der genauen Versionsnummer *ohne* Manifest-Abhängigkeit ist RtlGetVersion die beste Wahl.
// Wenn ein Manifest vorhanden ist, kann GetVersionEx genutzt werden, ist aber immer noch nicht optimal für Editionen.
// Lassen wir den Teil für GetWindowsVersionInfo zur Verdeutlichung weg, da er oft zu Verwirrung führt.
// Der wichtigste Punkt ist, dass VerifyVersionInfo die *Version des Aufrufers* (wie im Manifest) vergleicht,
// es sei denn, man verwendet VER_GREATER_EQUAL mit einer bekannten aktuellen Build-Nummer.
// Aber selbst dann *ermittelt* es nicht die Build-Nummer, sondern *prüft*, ob sie größer/gleich ist.
// Die beste Herangehensweise, um eine vergleichbare Detailliertheit wie RtlGetVersion zu erreichen,
// ist tatsächlich der Aufruf von GetVersionEx WENN ein aktuelles Manifest vorhanden ist.
// Dann wird es die tatsächlichen Major/Minor/Build-Nummern liefern.
// Danach kann GetProductInfo für die Edition verwendet werden.
// Wir gehen davon aus, dass ein korrektes Manifest vorhanden ist, wenn wir GetVersionEx aufrufen,
// um eine präzise OSVERSIONINFOEX zu erhalten.
BOOL bResult = GetVersionEx((LPOSVERSIONINFO)osVersionInfo);
if (!bResult) {
printf(„GetVersionEx failed with error %lu.n”, GetLastError());
return FALSE;
}
return TRUE;
}
// Hilfsfunktion zur Ermittlung des Produkttyps
const char* GetProductTypeName(DWORD dwProductType) {
switch (dwProductType) {
case PRODUCT_ULTIMATE: return „Ultimate Edition”;
case PRODUCT_PROFESSIONAL: return „Professional Edition”;
case PRODUCT_HOME_PREMIUM: return „Home Premium Edition”;
case PRODUCT_HOME_BASIC: return „Home Basic Edition”;
case PRODUCT_ENTERPRISE: return „Enterprise Edition”;
case PRODUCT_BUSINESS: return „Business Edition”;
case PRODUCT_STARTER: return „Starter Edition”;
case PRODUCT_CLUSTER_SERVER: return „Cluster Server Edition”;
case PRODUCT_DATACENTER_SERVER: return „Datacenter Server Edition”;
case PRODUCT_EMBEDDED: return „Embedded Edition”;
case PRODUCT_EMBEDDED_COMPACT: return „Embedded Compact Edition”;
case PRODUCT_ENTERPRISE_N: return „Enterprise N Edition”;
case PRODUCT_PROFESSIONAL_N: return „Professional N Edition”;
case PRODUCT_SERVER_STANDARD: return „Server Standard Edition”;
case PRODUCT_SERVER_DATACENTER: return „Server Datacenter Edition”;
case PRODUCT_HOME_SERVER: return „Home Server Edition”;
case PRODUCT_STORAGE_STANDARD: return „Storage Standard Edition”;
case PRODUCT_STORAGE_WORKGROUP: return „Storage Workgroup Edition”;
case PRODUCT_SMALLBUSINESS_SERVER: return „Small Business Server Edition”;
case PRODUCT_ESSENTIALBUSINESS_SERVER_MGMT: return „Essential Business Server Management Edition”;
case PRODUCT_ESSENTIALBUSINESS_SERVER_ADDL: return „Essential Business Server Additional Edition”;
case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM: return „Small Business Server Premium Edition”;
case PRODUCT_ENTERPRISE_E: return „Enterprise E Edition”;
case PRODUCT_N: return „N Edition”;
case PRODUCT_PRO_WORKSTATION: return „Pro for Workstations Edition”;
case PRODUCT_PRO_FOR_EDUCATION: return „Pro for Education Edition”;
// Weitere Produkttypen hinzufügen, je nach Bedarf
default: return „Unbekannte Edition”;
}
}
void LogWindowsVersionOfficial() {
OSVERSIONINFOEX osvi;
DWORD dwProductType;
// Verwende GetWindowsVersionInfo (basierend auf GetVersionEx mit angenommenem Manifest)
// oder besser: Rufe RtlGetVersion auf (siehe oben), um die osvi zu füllen.
// Da wir hier den „offiziellen” Weg zeigen, nutzen wir die Annahme eines korrekten Manifests für GetVersionEx.
// Die robustere Alternative bleibt RtlGetVersion!
if (GetWindowsVersionInfo(&osvi)) { // Ersetze dies idealerweise durch GetWindowsVersionRtl für Genauigkeit
// Ermittle den Produkttyp
if (GetProductInfo(osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.wServicePackMajor, osvi.wServicePackMinor, &dwProductType)) {
const char* productTypeName = GetProductTypeName(dwProductType);
printf(„OS Version: %d.%d (Build %d)n”,
osvi.dwMajorVersion,
osvi.dwMinorVersion,
osvi.dwBuildNumber);
printf(„Product Type: %sn”, productTypeName);
printf(„Service Pack: %s (Major %d, Minor %d)n”,
osvi.szCSDVersion,
osvi.wServicePackMajor,
osvi.wServicePackMinor);
printf(„Platform ID: %dn”, osvi.dwPlatformId);
// … Hier würde die Log-Funktion aufgerufen
} else {
printf(„Fehler beim Ermitteln des Produkttyps. Fehlercode: %lun”, GetLastError());
}
} else {
printf(„Fehler beim Ermitteln der Windows-Version.n”);
}
}
// Eine Main-Funktion zur Demonstration
int main() {
printf(„— Ermittlung mit RtlGetVersion (empfohlen) —n”);
LogWindowsVersion();
printf(„n— Ermittlung mit GetVersionEx (mit Manifest) + GetProductInfo —n”);
LogWindowsVersionOfficial();
return 0;
}
„`
Die `GetProductInfo`-Funktion gibt eine `DWORD` zurück, die einen Produkttyp-Code darstellt. Diese Codes können dann in einer `switch`-Anweisung oder einer Lookup-Tabelle in menschenlesbare Namen umgewandelt werden, wie im Beispiel `GetProductTypeName`. Dies liefert wertvolle Informationen über die genaue Windows-Edition, was besonders bei der Unterscheidung zwischen Client- und Server-Betriebssystemen oder verschiedenen Editionen (z.B. Home vs. Pro vs. Enterprise) wichtig ist.
### Umgang mit Build-Nummern, Service Packs und Editionen
Moderne Windows-Versionen, insbesondere Windows 10 und Windows 11, legen einen stärkeren Fokus auf die Build-Nummer anstatt nur auf die Major- und Minor-Version. Beide haben eine Major-Version von 10 und eine Minor-Version von 0. Was sie unterscheidet, ist die Build-Nummer:
* Windows 10, Version 2004 hat z.B. eine Build-Nummer von 19041.
* Windows 10, Version 21H2 hat z.B. eine Build-Nummer von 19044.
* Windows 11 hat z.B. eine Build-Nummer von 22000 oder höher.
Die `wServicePackMinor` in der `OSVERSIONINFOEXW`-Struktur wird seit Windows 10 oft als „Update Build Revision” (UBR) verwendet, die inkrementell mit kleineren Updates steigt. Es ist daher unerlässlich, diese Informationen ebenfalls in Ihren Logs festzuhalten.
Die Windows-Edition, die Sie über `GetProductInfo` erhalten, liefert weitere wichtige Kontextinformationen. Eine „Windows 10 Pro”-Installation unterscheidet sich in Bezug auf verfügbare Features und Gruppenrichtlinien von einer „Windows 10 Home”- oder „Windows 10 Enterprise”-Version. Diese Unterscheidungen können für das Debugging und den Support Ihrer Anwendung von größter Bedeutung sein.
Ein vollständiger Versionsstring für Ihre Log-Dateien könnte daher so aussehen:
`OS_Version: Windows 11 Pro (10.0.22621.1702 – Build 22621, UBR 1702)`
oder
`OS_Version: Windows 10 Enterprise (10.0.19045.3086 – Build 19045, UBR 3086)`
### Implementierung im Log-System
Die Ermittlung der Windows-Versionsnummer sollte idealerweise einmalig beim Programmstart Ihrer Anwendung erfolgen. Speichern Sie die ermittelten Informationen in global zugänglichen Variablen oder einer Singleton-Klasse, die von Ihrem Logging-Framework verwendet werden kann. Dadurch vermeiden Sie unnötige API-Aufrufe und stellen sicher, dass alle Log-Einträge konsistente Versionsinformationen enthalten.
Fügen Sie diese Informationen als festen Bestandteil jedes Log-Eintrags hinzu oder als eine Kopfzeile in der Log-Datei, die nur einmal am Anfang ausgegeben wird. Letzteres ist oft praktischer, da es die Log-Dateien nicht unnötig aufbläht, aber die Informationen sofort sichtbar sind.
**Beispiel für einen Log-Eintrag:**
„`
[2023-10-27 10:30:05.123][INFO] OS: Windows 11 Pro (10.0.22621.1702) – Application started.
[2023-10-27 10:30:10.456][ERROR] Failed to load config file: ‘settings.json’ (Error Code: 0x80070002)
„`
Denken Sie auch an eine robuste Fehlerbehandlung für die API-Aufrufe. Sollte `LoadLibraryA` oder `GetProcAddress` fehlschlagen, loggen Sie dies ebenfalls, damit Sie wissen, warum die Versionsinformationen fehlen.
### Best Practices und Tipps
* **Verwenden Sie `RtlGetVersion`**: Für die zuverlässigste und einfachste Ermittlung der *tatsächlichen* Windows-Versionsnummer, unabhängig vom Manifest, ist `RtlGetVersion` die erste Wahl.
* **Beachten Sie das Manifest**: Wenn Sie aus Kompatibilitätsgründen oder aufgrund von Unternehmensrichtlinien `GetVersionEx` nutzen *müssen*, stellen Sie sicher, dass Ihr Anwendungsmanifest die `supportedOS`-Einträge für alle relevanten Windows-Versionen enthält.
* **Loggen Sie Build-Nummer und Edition**: Die Major- und Minor-Versionen allein reichen nicht mehr aus. Build-Nummern und Produkttypen sind entscheidend für eine präzise Diagnose.
* **Zentralisieren Sie die Logik**: Erstellen Sie eine dedizierte Funktion oder Klasse, die für die Abfrage und Formatierung der Versionsinformationen zuständig ist.
* **Testen Sie auf verschiedenen Versionen**: Verlassen Sie sich nicht nur auf Ihre Entwicklungsumgebung. Testen Sie Ihre Versionsermittlung auf verschiedenen Windows-Versionen und -Editionen, um sicherzustellen, dass sie korrekt funktioniert.
* **Internationale Unterstützung**: Wenn Ihre Anwendung in verschiedenen Sprachen läuft, kann es nützlich sein, die Spracheneinstellungen des Betriebssystems ebenfalls zu loggen.
### Fazit
Ein gut implementiertes Logging-System ist das Rückgrat jeder stabilen Software. Die korrekte Ermittlung der Windows-Versionsnummer mittels C-API ist dabei ein oft übersehener, aber absolut kritischer Aspekt, der Ihnen bei der Fehlerbehebung, dem Benutzersupport und der Produktentwicklung unschätzbare Dienste leisten wird.
Indem Sie die Fallstricke veralteter APIs kennen und die leistungsstarken, modernen Funktionen wie `RtlGetVersion` oder die Kombination aus `GetVersionEx` (mit korrektem Manifest) und `GetProductInfo` nutzen, können Sie sicherstellen, dass Ihre Log-Dateien immer die präzisen und vollständigen Informationen enthalten, die Sie benötigen. Investieren Sie die Zeit in diese Implementierung – es wird sich langfristig auszahlen und die Zuverlässigkeit Ihrer Anwendungen erheblich steigern.