Laden...

In welchem Windows läuft das Programm (Win10 oder 11)

Letzter Beitrag vor einem Jahr 32 Posts 1.180 Views
In welchem Windows läuft das Programm (Win10 oder 11)

Ich möchte in einem Programm ermitteln, ob das Programm unter Windows 10 oder 11 (besser in welchem Windows XP, 7, 8.1. 10, 11) läuft.

Das Programm mache ich mit C# unter VS2022.

Es geht eigentlich ganz einfach mit

string description = RuntimeInformation.OSDescription;

Hier die Ergebnisse:

Windows 10 liefert ⇒ Microsoft Windows 10.0.19045 <= Build

Windows 11 liefert ⇒ Microsoft Windows 10.0.22621 <= Build

Muss man nun abfragen ; Build > 22000 = Win11, ansonsten Win10 ?

hk

Dazu gibst tausende von verschiedener Möglichkeiten.

Siehe Google Suche nach .NET detect windows Version

Und ja, die Buildnummern muss man immer beachten.

Du solltest nicht einen String abfragen, sondern direkt die Versionsnummer:

Version version = Environment.OSVersion.Version;

Noch besser

int buildNo = Environment.OSVersion.Version.Build

Da wir nicht wissen, wie die Versionsnummern in der Zukunft weitergezählt werden, sollte man aber auch zwingend Major und Minor daraufhin prüfen, ob diese auch wirklich 10 bzw. 0 sind. Sonst ist die Verwirrung groß wenn (warum auch immer) Windows 12 die Version 10.1.12345.0 hat.

Hat die Blume einen Knick, war der Schmetterling zu dick.

Die BuildId ist das einzig Konstante in der Windows Versionierung.

  • Windows 11 >20000
  • Windows 10 >10000
  • Windows 8.1 >9000
  • Windows XP >3000

Major und Minor sind bei Windows schon immer (wie bei der meisten Software) Marketing-Versionen.
Windows hält sich hier inhaltlich nicht an SemVer. BuildId wird aber mit jedem CI-Build unique erzeugt.

Glaskugelraten: sehr wahrscheinlich wird Windows 12 nicht mit 30xxx anfangen, da an Windows viel mehr und schneller gearbeitet wird.
Wird also vermutlich ein anderer Range werden; wer weiß, vielleicht bei 100xxx oder sowas.

Version 10.0.x kann übrigens sowohl Windows 10 sein wie auch Windows Server 2016 bzw. 2019.

Wenn der Build bei 100.000 anfangen soll wird dann auch die Struktur in VERSIONINFO geändert?

Dort wird die Version ja als 64bit Wert angelegt (4x 16bit = 0..65535).

Meine Glaskugel ist in dem Bereich aber ganz wolkig, somit lassen wir uns überraschen.

Hat die Blume einen Knick, war der Schmetterling zu dick.

Ändert an der Situation ja nichts.

Davon abgesehen wird dem Windows Team herzlich egal sein wie das .NET Team die Version als Objekt implementiert hat.

Ich sprach eigentlich von diesem VERSIONINFO und nicht von System.Version, denn dort passt es problemlos, weil dort für Build (und auch die anderen Teile) ein Int32 verwendet wird.

Hat die Blume einen Knick, war der Schmetterling zu dick.

Ich habe jetzt eine Lösung gefunden, da muss man etwas mehr tun, als nur eine Varibale abfragen.

Das Ergenis dieses Test-Konsolenprogrammes sieht so aus:

Windows 10 Version 22H2 (Build 19045.3031)

Betriebssystemname      Microsoft Windows 10 Professional X64
Installiert am          22.10.2022 17:24:18
Registrierter Benutzer  xyz@t-online.de

In den Projekt Eigenschaften von VS2022 muss bei Build als Zielplattform X64 eingestellt sein.

using System;
using System.Runtime.InteropServices;

namespace WinVerHK
{
    internal class Program
    {
        static void Main(string[] args)
        {
            string sWinVer = WinVer();
            Console.ReadLine();
        }

        //===============================================
        // Windows-Version ermitteln
        //===============================================
        private static string WinVer()
        {
            string sCurrentBuild = "";
            string sUBR = "";
            string sDisplayVersion = "";
            string sEditionID = "";
            string sProductName = "";
            string sRegisteredOwner = "";
            string sReleaseId = "";
            string sInstallDate = "";
            string sInstallTime = "";
            string sCurrentMajorVersionNumber = "";
            string sCurrentMinorVersionNumber = "";
            string sCurrentVersion = "";
            string sSrvComment = "";

            string sVer = "XP"; // "XP", "7", "8.1", "10", "11"

            string OSa = RuntimeInformation.OSArchitecture.ToString(); // X64
            string sArchitektur = OSa;

            string osVersion = Environment.OSVersion.ToString();
            // 10: "Microsoft Windows NT 6.2.9200.0"
            // 11: "Microsoft Windows NT 6.2.9200.0"
            // 7 : "Microsoft Windows NT 6.1.7601 Service Pack 1"
            string sOsVersion = osVersion;

            string Version = Environment.Version.ToString();
            // 10: "4.0.30319.42000"
            // 11: "4.0.30319.42000"
            //  7: "4.0.30319.42000"
            string sVersion = Version;

            string OSd = RuntimeInformation.OSDescription;
            // 10 : Microsoft Windows 10.0.19045
            // 11 : Microsoft Windows 10.0.22621 = Build
            // 8.1: Microsoft Windows 6.3.9600 
            // 7  : Microsoft Windows 6.1.7601 S
            string sDescription = OSd;

            sVer = "???";

            if (OSd.StartsWith("Microsoft Windows 6.3.")) { sVer = "8.1"; goto UpNxt; }
            if (OSd.StartsWith("Microsoft Windows 6.1.")) { sVer = "7"; goto UpNxt; }

            string subKey1 = @"SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters";
            Microsoft.Win32.RegistryKey skey1 = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(subKey1);

            sSrvComment = HoleRegValue(skey1, "SrvComment");
            if (sSrvComment.EndsWith("10")) sVer = "10";
            if (sSrvComment.EndsWith("11")) sVer = "11";

          UpNxt:
            string subKey2 = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion";
            Microsoft.Win32.RegistryKey skey2 = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(subKey2);

            sCurrentBuild = HoleRegValue(skey2, "CurrentBuild");
            sUBR = HoleRegValue(skey2, "UBR");
            sDisplayVersion = HoleRegValue(skey2, "DisplayVersion");
            sEditionID = HoleRegValue(skey2, "EditionID");
            sProductName = HoleRegValue(skey2, "ProductName");
            sRegisteredOwner = HoleRegValue(skey2, "RegisteredOwner");
            sReleaseId = HoleRegValue(skey2, "ReleaseId");
            sInstallDate = HoleRegValue(skey2, "InstallDate");
            sInstallTime = HoleRegValue(skey2, "InstallTime");
            sCurrentMajorVersionNumber = HoleRegValue(skey2, "CurrentMajorVersionNumber");
            sCurrentMinorVersionNumber = HoleRegValue(skey2, "CurrentMinorVersionNumber");
            sCurrentVersion = HoleRegValue(skey2, "CurrentVersion");


            Console.WriteLine("Windows " + sVer + " Version " + sDisplayVersion +
                              " (Build " + sCurrentBuild + "." + sUBR + ")\n");
            string SysName = sOsVersion.Substring(0, 18) + sVer + " " + sEditionID + " " + sArchitektur;
            Console.WriteLine("Betriebssystemname      " + SysName);
            Console.WriteLine("Installiert am          " + sInstallDate);
            Console.WriteLine("Registrierter Benutzer  " + sRegisteredOwner);

            return sVer; // 10 oder 11 oder 8.1 oder 7 oder XP

        } // private static string WinVer() 

        //=============================================================
        // Angaben zu einem Key aus der Registry holen
        //=============================================================
        private static string HoleRegValue(Microsoft.Win32.RegistryKey skey, string suKey)
        {
            // Problem: Groß- und Kleinschreibung ist nicht immer gleich, bei C# aber relevant
            System.String[] ParaKeys = skey.GetValueNames();
            for (int i = 0; i < ParaKeys.Length; i++)
            {
                if (suKey.ToLower() == ParaKeys[i].ToLower())
                {
                    string rc = skey.GetValue(ParaKeys[i]).ToString(); // Original-Key benutzen

                    if (suKey == "InstallDate") // UNIX-Zeistempel ab dem 01.01.1970 00:00:00
                    {
                        DateTime AnfDate = new DateTime(1970, 1, 1);
                        var sec = skey.GetValue(ParaKeys[i]);   // UINT32 = Sekunden seit dem 01.01.1970 00:00:00 Uhr
                        DateTime EndDate = AnfDate.AddSeconds((int)sec);
                        rc = EndDate.ToString();
                    }
                    if (suKey == "InstallTime") // Windows-Zeitstempel (?) ab dem 01.01.1600 00:00:00
                    {
                        DateTime MilliSec = new DateTime((long)skey.GetValue(ParaKeys[i]));  // UINT64 = ulong MilliSekunden seit 1600
                        DateTime EndDate = MilliSec.AddYears(1600); // 01.01.1600 dazu zählen
                        rc = EndDate.ToString();
                    }
                    return rc;
                }
            }  // for

            return "";

        } // private static string HoleRegValue
    }
}

hk

In Deiner Zeiterstellung ist aber mindestens ein Fehler drin, nämlich die Zeitzonenbehandlung. Du erzeugst alles mit der lokalen Zeitzone (zB Deutschland), Windows speichert jedoch alle Werte getreu dem ISO-Standard nach UTC.

[FAQ] DateTime vs. DateTimeOffset und der Umgang mit Zeiten in .NET

Aber was soll die Abfrage des RegistryKeys HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters\SrvComment mit der Windows-Version zu tun haben?

Bei mir z.B. gibt es diesen Key garnicht.

Generell frage ich mich, wozu das sein soll?

Würde es nicht reichen, die Version auf Namen zu mappen? Dann suchst Du dir einmal eine Tabelle raus und schreibst das in Code, das ist etwas arbeit, aber weit weniger fehleranfällig und komplex. Dann hast Du natürlich nicht die Info, von wem und wann es installiert wurde, aber brauchst Du das überhaupt?

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

@Abt,
Die absolute Zeit ist doch völlig uninteressant, ob die Installation nun eine Stunde früher oder später war - interessant ist das Datum.
Die Uhrzeit ist trotzdem hilfreich, wenn ich z.B. nachschauen will, ob ich nun zuerst Windows 11 oder 10 auf einem PC installiert habe. Bei mir gibt es beinahe auf jedem PC Win10 und 11 parallel und zusätzlich XP, 7 und 8.1 und Ubuntu als VMs. DOS und Win3.1 habe ich auch noch in der DosBox laufen.

@Th69,
Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters
SrvComment = Windows 10

Aus dieser Angabe ermittle ich, ob es sich um Windows 10 oder 11 handelt, Bei beiden meiner Pro-Versionen gibt es diesen Parameter. Bei Windows 7 und 8.1 gibt es ihn nicht, deshalb frage ich bei diesen Versionen den Parameter nicht ab, Win7 und 8.1 sind bereits an Hand der OSDescription eindeutig zu erkennen. WIn10 und 11 leider nicht.

Wenn bei Dir SrvComment nicht vorhanden ist - dann hast Du sicherlich ein anderes WIndows, das habe ich nicht und konnte es deshalb auch nicht testen.

@Palladin007,
in meinem Programm, wo ich das einsetze, kann man die System-Information abrufen.
Die angezeigten Werte sind weitestgehend identisch mit der System-Info von Windows 10.
Dort hat man (aus gutem Grund ?) die Uhrzeit der Installation weggelassen.

hk

Das ist ein sehr interessanter Umgang mit Feedback zu Code-Fehler.

Ich könnte bei Zeit ja noch den Hinweis "Lokalzeit" ausgeben.

Welche Zeit sollte denn angezeigt werden, um den CODE-FEHLER zu beseitigen ?

Da ja nicht angespeichert ist, wo (an welchem Ort der  Erde) die  Installation stattfand, kann man die Zeit auch nicht korrekt zuordnen. Wurde dieses Windows bei einem fertig-PC/Notebook/Tablet in Mexiko, China oder Indien installiert und war es damals an diesem ort gerade 14°° Uhr Ortszeit, was soll man nun in Deutschlan anzeigen ? Man weiß ja nicht WO bei dem betreffenden Zeitstempel installiert wurde.

Die ZEIT und die ZEITZONE ist m.E. mehr ein philosophisches als ein technisch-mathematisches Problem.

hk

Zitat von hkdd

Da ja nicht angespeichert ist, wo (an welchem Ort der  Erde) die  Installation stattfand, kann man die Zeit auch nicht korrekt zuordnen.

Egal, ist nicht relevant.

Abt schrieb doch, dass Windows die UTC Zeit speichert, die ist weltweit gültig.
Du musst es also als UTC lesen und als lokale Zeit ausgeben.

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

Ich könnte bei Zeit ja noch den Hinweis "Lokalzeit" ausgeben.

Welche Zeit sollte denn angezeigt werden, um den CODE-FEHLER zu beseitigen ?

Du kannst auch einfach die Zeit korrekt einlesen, nach UTC. Ein kleiner Edit behebt den Fehler.
Deswegen hab ich Dir den Link gegeben.

DateTime dt = new(1970, 1, 1); // TimeZone Unknown
DateTime dt = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); // 1970 nach UTC
DateTimeOffset o = new(1970, 1, 1, 0, 0, 0, 0, 0, TimeSpan.Zero); // 1970 nach UTC

Die ZEIT und die ZEITZONE ist m.E. mehr ein philosophisches als ein technisch-mathematisches Problem.

Nein. Das ist ein messbarer Fakt. UTC ist standardisiert und ist weltweit gleich.
Für Deine Information spielt der Ort auch keine Rolle.

Da ja nicht angespeichert ist, wo (an welchem Ort der  Erde) die  Installation stattfand, kann man die Zeit auch nicht korrekt zuordnen.

Das wiederum ist Theorie. Das Argument eine Zeit passend zu seinem Ort zu speichern ist ein nett verwendetes Argument, zB auch vom EF Core Team (das anders argumentiert als das MSSQL Server Team), das in der Realität jedoch kaum eine Rolle spielt und eine Genauigkeit auch nicht erreicht werden kann.
When “UTC everywhere” isn’t enough - storing time zones in PostgreSQL and SQL Server
Das ist eine nette Theorie, die die gleiche Schwäche hat, wie das Argument selbst. Es würde nur funktionieren, wenn die Geokoordinaten zu einem Zeitstempel gespeichert werden und dementsprechend Zeitzonen zur Verfügung stehen würden - dem ist aber nicht so.
Eine Zeitzone zu einem Zeitstempel zu speichern hat keinen Vorteil im Bezug zum Ort, denn der Ort könnte dieser Theorie die Zeitzone ebenfalls wechseln - und die Genauigkeit ist wieder verloren. Das ist aber eine Theorie, die 1-2 mal pro 100 Jahre eintritt (siehe Süd/Nordkorea).
Hingegen die (Konvertierungs-)Fehler von DateTime gegenüber DateTimeOffset bleiben die gleichen.

Nach UTC bzw. nach ISO8601 zu speichern sind jedoch technische Standards - daher auch keine Philosophie.
Wenn Du also eine Zeit erhälst, die diesem Standard zugrunde liegt, dann machts schon sinn das auch so zu behandeln; aber zu argumentieren "dann isses halt ne Stunde falsch" ist halt schon... "ein schwieriger Umgang".

@Abt,

Windows speichert jedoch alle Werte getreu dem ISO-Standard nach UTC.

Meinst Du damit alle Zeiten, also auch den Zeitstempel beim Ändern von Dateien ?

Ich habe gerade einen Ordner angelegt mit dem Namen X162739,

dabei bedeuten die Zahl die Uhrzeit des Anlegens = 16:27:39

Im Directory sieht der Zeitstempel folgendermaßen aus:

000EC0: 58 31 36 32  37 33 39 20  20 20 20 10  00 0D 76 83 :X162739    ...v.:
000ED0: CB 56 CB 56  00 00 77 83  CB 56 C8 02  00 00 00 00 :.V.V..w..V......:
                           -----  -----
                           Time   Date 

TIME: 77 83 = 0x8377 = 1000 0011 0111 0111
                       ------#######++++++
                       Stunde Minute DoppelSek  = 16:27:46
                       10000  011011 10111
                       16     27     23 => *2 = 46

Die Sekunden weichen etwas ab, aber nicht die Stunde.

Das DIR Kommando zeigt es so an, ich kann keine UTC-Zeit erkennen, alle Zeiten sind dresdner Ortszeiten, auch die BIOS/UEFI-Uhr läuft nach unserer Ortszeit:

Microsoft Windows [Version 10.0.19045.3031]
(c) Microsoft Corporation. Alle Rechte vorbehalten.

G:\Test>dir a:
 Datenträger in Laufwerk A: ist AAA
 Volumeseriennummer: EC88-CC58

 Verzeichnis von A:\

08.06.2023  16:02    <DIR>          Bilder
11.06.2023  16:27    <DIR>          X162739
               0 Datei(en),              0 Bytes
               2 Verzeichnis(se),          2.048 Bytes frei

G:\Test>

hk

Muss man bei Windows einstellen, dass man mit der UTC Zeit arbeiten möchte ?

Windowspage - Datum und Uhrzeit - BIOS/CMOS-Zeit als UTC-Zeit festlegen

Bei mir ist diesbezüglich nichts in der Registry eingestellt, meine Uhrzeit läuft als mitteleuropäische Sommerzeit (UTC+2)

Deshalb sind die angezeigten Zeiten immer die zutreffenden Ortszeiten meines ortsfesten PCs.

Nach UTC bzw. nach ISO8601 zu speichern sind jedoch technische Standards - daher auch keine Philosophie.

Der eine macht es so, der andere macht es nicht so. Das ist kein fixer Standard. Windows mit UTC Zeiten haben nur die PCs, bei denen man es so einstellt, ein Standard-Windows benutzt die Ortszeit. Anders ist es ggf. bei einigen Linux-Systemen.

Th69, wieso hat Deine Registry nicht diesen Eintrag ? Bei mir ist der bei allen drei WIndows 10 und 11 PC vorhanden, allerdings nicht bei der Vorgängerversionen XP, 7 und 8.1.

hk

Wieso reden wir plötzlich von Dateien? Wir reden von der Windows Systemzeit.

Muss man bei Windows einstellen, dass man mit der UTC Zeit arbeiten möchte ?

Windows arbeitet intern immer mit einer standardisierten Zeit, die Dich aber nicht interessiert. Dich interessiert, was Windows für eine Zeit zurück gibt, wenn Du einen Wert von der API oder einer Ausgabe haben willst.
Und ja, diese Werte unterscheiden sich in der Zeitzone, sogar leider nicht nur technologisch (NTFS zB arbeitet immer nach UTC) sondern sogar pro Methode. Alte Methoden verwenden Local Time, neue Methoden / Rückgaben richten sich nach UTC.
Es ist also völlig unerheblich, ob Windows nach UTC intern arbeitet oder nach der lokalen Zeit: einzig und allein das, was Dir Windows (bereits angepasst/nicht angepasst) gibt, ist relevant.

Was also Deine Konsole ausgibst: auch völlig egal.
Die Konsole gibt Dir nicht mal die Rohwerte der NTFS-Table aus, sondern sehr wahrscheinlich angepasst auf Deine UI - wie alle UI Ausgaben, die zuerst nach der Region-Format-Pipeline laufen. Also die Anpassung der Zeit an die Zeitzone, die Du dem aktuellen Benutzer hinterlegt hast.

Das gleiche Prinzip verwendet man in .NET:
Alle Zeiten intern sollten immer nach UTC behandelt werden; erst bei der Ausgabe werden diese an die Zeitzone des Benutzers angepasst/formatiert.
Kannst den .NET Guidelines entnehmen (gleiches, dass man ToString nicht ohne Culture verwenden soll); ist auch nicht in nur in .NET so sondern - weil Industriestandard - bei allen gängigen Runtimes.

Kannst natürlich nun sagen: mir egal. Ändert aber halt nix dran.

Der Registryeintrag ist nur die über das Netzwerk angezeigte Computerbeschreibung, s. "Erweiterte Systemeinstellungen" sowie How do I set the comment for my machine that is displayed in Network Neighborhood? (bzw. auf deutsch Computerbeschreibung über das Netzwerk ändern )

Wenn du da "Windows 10" oder "Windows 11" reingeschrieben hast, dann ist das aber nicht allgemeingültig (und bei mir ist dieser eben leer).

Edit: Nach eigener Recherche ist wohl RtlGetVersion die derzeit beste Möglichkeit, um Windows 11 zu erkennen, s.a. Windows 11 Version Detection .

@Abt,

ich habe es wie folgt geändert

                    if (suKey == "InstallDate") // UNIX-Zeistempel ab dem 01.01.1970 00:00:00
                    {
                        DateTime AnfDate = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
                        var sec = skey.GetValue(ParaKeys[i]);   // UINT32 = Sekunden seit dem 01.01.1970 00:00:00 Uhr
                        DateTime EndDate = AnfDate.AddSeconds((int)sec);
                        rc = EndDate.ToString();

                    }

Es wird weiterhin 17:24:18 als Uhrzeit der Installation angezeigt.

Windows 10 Version 22H2 (Build 19045.3031)

Betriebssystemname      Microsoft Windows 10 Professional X64
Installiert am          22.10.2022 17:24:18

hk

Th69,

auf meinem Tablet steht dort auch nicht "Windows 10", sondern etwas anderes - ich habe das allerdings nicht selbst eingetragen.

Ich benutze nun Deinen Vorschlag und bekomme damit heraus, um welches Windows es sich handelt

using System;
using System.Runtime.InteropServices;

namespace WinVerHK
{
    internal class Program
    {
        struct OSVERSIONINFOEXW 
        {
            public int dwOSVersionInfoSize { get; set; }
            public int dwMajorVersion { get; set; }
            public int dwMinorVersion { get; set; }
            public int dwBuildNumber { get; set; }
        }

        [DllImport("ntdll.dll", SetLastError = true)]
        static extern int RtlGetVersion(ref OSVERSIONINFOEXW versionInfo);


        static void Main(string[] args)
        {
            string sWinVer = WinVer();
            Console.ReadLine();
        }

        //===============================================
        // Windows-Version ermitteln
        //===============================================
        private static string WinVer()
        {
            OSVERSIONINFOEXW osv = new OSVERSIONINFOEXW();
            var rcRtl = RtlGetVersion(ref osv);

            if (osv.dwMajorVersion == 10 && osv.dwMinorVersion == 0  && 
                osv.dwBuildNumber  >= 22000) 
            { osv.dwMajorVersion = 11; }
            
            string RtlVers = osv.dwMajorVersion.ToString() + "." +
                             osv.dwMinorVersion.ToString();

            string sVer = "??"; // "XP", "7", "8.1", "10", "11"

            switch (RtlVers) 
            {
                case "5.1" : sVer = "XP";    break;
                case "6.0" : sVer = "Vista"; break;
                case "6.1" : sVer = "7";     break;
                case "6.2" : sVer = "8";     break;
                case "6.3" : sVer = "8.1";   break;
                case "10.0": sVer = "10";    break;
                case "11.0": sVer = "11";    break;
            }

hk

Ich habe nun allerdings noch ein Problem, auch eine weniger elegante Lösung.

Ich möchte das Programm nicht im X64 Modus ausführen, weil es da z.B. auf meinem Tablet mit Win10-32 nicht funktioniert.

Wenn ich bei VS2022 in den Eigenschaften "Any CPU" statt X64 einstelle, erhalte ich ganz andere Angaben beim Auslesen der Registry.

Da stimmen einigen Angaben nicht oder sind gar nicht vorhanden, z.B. Installations-Zeitpunkt.  Es wird von folgendem Key gelesen

"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion"

anstelle von

"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion"

Mein Lösungsansatz ist folgender: Ich rufe regedit mit folgendem Kommando auf

"%WINDIR%\RegEdit" /a "x:\RegData.TEMP" "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion"

und lese danach die Datei x:\RegData.TEMP ein und hole mir die benötigten Daten heraus. In dieser Datei steht alles so, wie beim X64-Programm.

Meine Frage: Gibt es eine Möglicheit in einem X64-System mit einem AnyCPU-Programm die X64-Angaben mit GetValue auszulesen ?

Jetzt bekomme ich die unkorrekten WOW6432Node Werte.

hk

Wenn ich Dein Code vergleiche mit https://github.com/pruggitorg/detect-windows-version dann beachtest Du dutzende Dinge nicht (und es sieht nach Spaghetti-Code aus).
Ja, hab gesehen, dass das Projekt kein Windows 11 unterstützt (Missing newer OS versions) - geht ja auch einfach nur ums Prinzip. In dem Issue werden aber Win11 Detect Recommendations referenziert.

Wenn ich bei VS2022 in den Eigenschaften "Any CPU" statt X64 einstelle, erhalte ich ganz andere Angaben beim Auslesen der Registry.

Völlig normal. Genau so soll Windows und x64 funktionieren.

Wenn Du nicht weißt, was Any CPU bedeutet (was ich aus der Aussage schließe): .NET Docs - PlatformTarget
Bezüglich Windows: https://de.wikipedia.org/wiki/WOW64

Mit der RtlGetVersion kann ich die Windows-Version erkennen unabhängig vom Modus X64 oder AnyCPU.

AnyCPU bwirkt m.E., dass das Programm im x86 Modus ausgeführt wird und dass in diesem Fall die für diesen Modus vorhandenen Registry Werte zur Verfügung gestellt werden. Das ist bekannt und OK. Allerdings ist nicht OK, dass die Registry Werte

"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion"

mit denen von

"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion"

nicht übereinstimmen.

Im X64 Modus bekomme ich korrekte Werte, das Programm kann aber nicht in einem Win32 laufen, dort gibt es die beiden unterschiedlichen Werte nicht.

Im AnyCPU Modus funktioniert das Programm in einem Win32, erhält aber im Win-64 falsche Angaben, weil die Angaben bei WOW6432Node unkorrekt sind oder ganz fehlen, wie das Install-Datum.

Für andere Angaben in der Registry habe ich noch nicht auf Abweichungen untersucht.

Mein Ziel deshalb, ich möchte, dass das Programm auch im AnyCPU-Modus  die korrekten Werte erhält.

hk

Zitat von hkdd

AnyCPU bwirkt m.E., dass das Programm im x86 Modus ausgeführt wird und dass in diesem Fall die für diesen Modus vorhandenen Registry Werte zur Verfügung gestellt werden. Das ist bekannt und OK.

Nein, das macht AnyCPU pauschal so nicht.
Genau deswegen hab ich Dir den Link gegeben. Schade, dass Du es (offenbar?) nicht durchgelesen hast.

"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion"

mit denen von

"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion"

nicht übereinstimmen.

Völlig normal. Herzlich Willkommen in der Welt der Windows Registry.

Zum Verständnis, weil das in Deinem Code fehlt und Du offenbar auch das Verhalten der Registry zur Architecture nicht angeschaut hast:
Du kannst egal ob von x86 oder x64 (respektive AnyCPU) auf die jeweilige Registry zugreifen, indem Du bei OpenBaseKey die RegistryView angibst

RegistryKey reg32 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32);
RegistryKey key = reg32.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion");

Jetzt habe ich eine Lösung gefunden, das Programm zeigt alles korrekt an in Windows 32 und 64.

Was habe ich gemacht ?

Projekt → Eigenschaften → Build → Zielplattform: Any CPU

Projekt → Eigenschaften → Anwendung → Zielframework: .NET Framework 4 (vorher war 4.8)

Damit funktioniert alles.  Die Merkwürdigkeiten sind offenbar erst nach 4.0 aufgetreten.

Hier das jetzige Programm

using System;
using System.Runtime.InteropServices;

namespace WinVerHK
{
    internal class Program
    {
        struct OSVERSIONINFOEXW 
        {
            public int dwOSVersionInfoSize { get; set; }
            public int dwMajorVersion { get; set; }
            public int dwMinorVersion { get; set; }
            public int dwBuildNumber { get; set; }
        }

        [DllImport("ntdll.dll", SetLastError = true)]
        static extern int RtlGetVersion(ref OSVERSIONINFOEXW versionInfo);

        static void Main(string[] args)
        {
            string sWinVer = WinVer();
            Console.ReadLine();
        }

        //===============================================
        // Windows-Version ermitteln
        //===============================================
        private static string WinVer()
        {
            OSVERSIONINFOEXW osv = new OSVERSIONINFOEXW();
            var rcRtl = RtlGetVersion(ref osv);

            if (osv.dwMajorVersion == 10 && osv.dwMinorVersion == 0  && 
                osv.dwBuildNumber  >= 22000) 
            { osv.dwMajorVersion = 11; }
            
            string RtlVers = osv.dwMajorVersion.ToString() + "." +
                             osv.dwMinorVersion.ToString();

            string sVer = "??"; // "XP", "Vista", "7", "8", "8.1", "10", "11"

            switch (RtlVers) 
            {
                case "5.1" : sVer = "XP";    break;
                case "6.0" : sVer = "Vista"; break;
                case "6.1" : sVer = "7";     break;
                case "6.2" : sVer = "8";     break;
                case "6.3" : sVer = "8.1";   break;
                case "10.0": sVer = "10";    break;
                case "11.0": sVer = "11";    break;
            }
            string sCurrentBuild = osv.dwBuildNumber.ToString();

            bool Is64os = System.Environment.Is64BitOperatingSystem;
            string sArchitektur = Is64os ? "x64" : "x86";

            string subKey2 = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion";
            Microsoft.Win32.RegistryKey skey2 = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(subKey2);

            sCurrentBuild = HoleRegValue(skey2, "CurrentBuild");
            string sUBR = HoleRegValue(skey2, "UBR");
            string sDisplayVersion = HoleRegValue(skey2, "DisplayVersion");
            string sEditionID = HoleRegValue(skey2, "EditionID");
            string sRegisteredOwner = HoleRegValue(skey2, "RegisteredOwner");
            string sInstallDate = HoleRegValue(skey2, "InstallDate");

            Console.WriteLine("Windows " + sVer + " Version " + sDisplayVersion +
                              " (Build " + sCurrentBuild + "." + sUBR + ")\n");

            string SysName = "Microsoft Windows " + sVer + " " + sEditionID + " " + sArchitektur;

            Console.WriteLine("Betriebssystemname      " + SysName);
            Console.WriteLine("Installiert am          " + sInstallDate);
            Console.WriteLine("Registrierter Benutzer  " + sRegisteredOwner);

            return sVer; // 10 oder 11 oder 8.1 oder 7 oder XP

        } // private static string WinVer() 

        //=============================================================
        // Angaben zu einem Key aus der Registry holen
        //=============================================================
        private static string HoleRegValue(Microsoft.Win32.RegistryKey skey, string suKey)
        {
            // Problem: Groß- und Kleinschreibung ist nicht immer gleich, bei C# aber relevant

            System.String[] ParaKeys = skey.GetValueNames();
            for (int i = 0; i < ParaKeys.Length; i++)
            {
                if (suKey.ToLower() == ParaKeys[i].ToLower())
                {
                    string rc = skey.GetValue(ParaKeys[i]).ToString(); // Original-Key benutzen

                    if (suKey == "InstallDate") // UNIX-Zeistempel ab dem 01.01.1970 00:00:00
                    {
                        DateTime AnfDate = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
                        var sec = skey.GetValue(ParaKeys[i]); // UINT32 = Sekunden seit 01.01.1970 00:00:00 Uhr
                        DateTime EndDate = AnfDate.AddSeconds((int)sec);
                        rc = EndDate.ToString();
                    }
                    return rc;
                }
            }  // for

            return "";

        } // private static string HoleRegValue
    } // internal class Program
} // namespace WinVerHK

hk

Du arbeitest damit mit einer seit Jahren abgekündigten .NET Version, die auf zukünftigen Windows Versionen nicht mehr funktioniert.

Kann man machen. Man kann aber auch statt dem Workaround einfach den Programmierfehler beheben.

@Abt, RegistryView ⇒

Danke für diesen Hinweis.

Jetzt sieht es so aus und funktioniert mit AnyCPU ...

using Microsoft.Win32;
using System;
using System.Runtime.InteropServices;

namespace WinVerHK
{
    internal class Program
    {
        struct OSVERSIONINFOEXW 
        {
            public int dwOSVersionInfoSize { get; set; }
            public int dwMajorVersion { get; set; }
            public int dwMinorVersion { get; set; }
            public int dwBuildNumber { get; set; }
        }

        [DllImport("ntdll.dll", SetLastError = true)]
        static extern int RtlGetVersion(ref OSVERSIONINFOEXW versionInfo);

        static void Main(string[] args)
        {
            string sWinVer = WinVer();
            Console.ReadLine();
        }

        //===============================================
        // Windows-Version ermitteln
        //===============================================
        private static string WinVer()
        {
            OSVERSIONINFOEXW osv = new OSVERSIONINFOEXW();
            var rcRtl = RtlGetVersion(ref osv);

            if (osv.dwMajorVersion == 10 && osv.dwMinorVersion == 0  && 
                osv.dwBuildNumber  >= 22000) 
            { osv.dwMajorVersion = 11; }
            
            string RtlVers = osv.dwMajorVersion.ToString() + "." +
                             osv.dwMinorVersion.ToString();

            string sVer = "??"; // "XP", "Vista", "7", "8", "8.1", "10", "11"

            switch (RtlVers) 
            {
                case "5.1" : sVer = "XP";    break;
                case "6.0" : sVer = "Vista"; break;
                case "6.1" : sVer = "7";     break;
                case "6.2" : sVer = "8";     break;
                case "6.3" : sVer = "8.1";   break;
                case "10.0": sVer = "10";    break;
                case "11.0": sVer = "11";    break;
            }
            string sCurrentBuild = osv.dwBuildNumber.ToString();

            bool Is64os = System.Environment.Is64BitOperatingSystem;
            string sArchitektur = Is64os ? "x64" : "x86";

            RegistryKey key = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
            RegistryKey skey2 = key.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion");

            sCurrentBuild = HoleRegValue(skey2, "CurrentBuild");
            string sUBR = HoleRegValue(skey2, "UBR");
            string sDisplayVersion = HoleRegValue(skey2, "DisplayVersion");
            string sEditionID = HoleRegValue(skey2, "EditionID");
            string sRegisteredOwner = HoleRegValue(skey2, "RegisteredOwner");
            string sInstallDate = HoleRegValue(skey2, "InstallDate");

            Console.WriteLine("Windows " + sVer + " Version " + sDisplayVersion +
                              " (Build " + sCurrentBuild + "." + sUBR + ")\n");

            string SysName = "Microsoft Windows " + sVer + " " + sEditionID + " " + sArchitektur;

            Console.WriteLine("Betriebssystemname      " + SysName);
            Console.WriteLine("Installiert am          " + sInstallDate);
            Console.WriteLine("Registrierter Benutzer  " + sRegisteredOwner);

            return sVer; // 10 oder 11 oder 8.1 oder 7 oder XP

        } // private static string WinVer() 

        //=============================================================
        // Angaben zu einem Key aus der Registry holen
        //=============================================================
        private static string HoleRegValue(Microsoft.Win32.RegistryKey skey, string suKey)
        {
            // Problem: Groß- und Kleinschreibung ist nicht immer gleich, bei C# aber relevant

            System.String[] ParaKeys = skey.GetValueNames();
            for (int i = 0; i < ParaKeys.Length; i++)
            {
                if (suKey.ToLower() == ParaKeys[i].ToLower())
                {
                    string rc = skey.GetValue(ParaKeys[i]).ToString(); // Original-Key benutzen

                    if (suKey == "InstallDate") // UNIX-Zeistempel ab dem 01.01.1970 00:00:00
                    {
                        DateTime AnfDate = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
                        var sec = skey.GetValue(ParaKeys[i]);   // UINT32 = Sekunden seit dem 01.01.1970 00:00:00 Uhr
                        DateTime EndDate = AnfDate.AddSeconds((int)sec);
                        rc = EndDate.ToString();
                    }
                    return rc;
                }
            }  // for

            return "";

        } // private static string HoleRegValue
    } // internal class Program
} // namespace WinVerHK

hk