Danke für den Hinweis auf die neue Implementierung von Json. Leider ist es auch so, dass man bei einer Google-Suche, auch über Google News nichts dazu findet, dass Newtonsoft Json praktisch Out of Date ist. Ich werde also natürlich die neue Variante nutzen.
Danke auch an thomas.at mit dem Klartext-Hinweis auf "{get; set;}". Da habe ich echt noch Probleme. NewtonSoft war da wohl etwas toleranter was sowas anging.
Was ist an System.Text.Json besser? Irgendwie wird immer auf das Json von Newtonsoft verwiesen. Da finden sich im Zweifel dann für mich als Anfänger sicher auch mehr Hilfe, weil es wohl jeder kennt und verwendet. Hat ja auch knapp 2,89B downloads bei Nu-Get, was wohl im deutschen 2,89 Millliarden? sind.
Ich habe das trotzdem mal probiert und wohl auch die Lösung gefunden, die mit Newtonsoft funktioniert.
Ich habe die Klasse "InventoryResponse" etwas umgeschrieben und die Klasse "Test" gelöscht. (Ist das richtig?)
class InventoryResponse
{
public int id;
}
Damit klappt das nun. (Diese "Test"-Klasse kam mir eh komisch vor, habe mich aber natürlich an das Lernbeispiel aus einem Videokurs orientiert.)
auskommentiert und von learn.microsoft.com das Beispiel für "System.Text.Json" versucht umzusetzen. Klappt soweit auch, aber die id ist jetzt "0" (als Zahl, nicht "null")
steht plötzlich der Wert "null", also "response | null"
Ich habe das Ganze auch mit
class Test
{
public string hardwareComputerSerial;
}
class InventoryResponse
{
public Test hardwareComputerSerial;
}
probiert. Passiert genau das Gleiche, nur dass dann der "Exception
Zitat
Newtonsoft.Json.JsonSerializationException: 'Error converting value "1A2B3C4" to type 'SysteminventoryV2.Api+Test'. Path 'hardwareComputerSerial', line 1, position 43.'
ArgumentException: Could not cast or convert from System.String to SysteminventoryV2.Api+Test.
This exception was originally thrown at this call stack:
[External Code]
steht.
Könnt ihr mir bitte sagen, was ich falsch mache und warum eine Zeile weiter unten plötzlich Einfluss auf eine Variable hat, die weiter oben ohne die Zeile weiter unten bereits den richtigen Wert hat, mit der Zeile dann aber nicht mehr?
Ich habe vor zwei Wochen angefangen mein Projekt auf Github zu laden. Ist aber private.
Nun habe ich weiter gemacht und wollte das Repository aktualisieren. Geht aber nicht, weil angeblich ist mein lokales Repository älter als online Repository auf GitHub. Stimmt aber nicht und selbst wenn wäre mir das egal. Ich kann machen was ich will, ich bekomme es nicht hin das Projekt auf Github zu aktualisieren. Einfach den alten Kram überschreiben. Jemand eine Idee? Kann doch eigentlich nicht so schwer sein? Verwende VS 2022 Community Edition.
Wenn ich erst Pulle gibt es Konflikte. Ich hake dann zwar an, dass ich das eingehende verwenden möchte, kann das aber nirgendwo bestätigen.
hast du mal im Debugger per Breakpoint geschaut, was ipv6key zurückgibt?
Ist das wirklich NULL?
Ja, ist "null". (Siehe Anhang) Ich würde es gerne verstehen, unabhängig davon, dass der restliche Code, bzw. Aufbau nicht schön ist. Direkt falsch ist es ja auch nicht.
Folgender Code kommt mir etwas lang vor. Kann man den sinnvoll kürzen?
Außerdem gibt es in Zeile 9 eine NullReferenceException.
Ist auch richtig, denn auf meinem privaten System gibt es den Registry-Eintrag nicht. Aber ich prüfe doch auf "null"? VS sagt aber auch, dass ich nicht prüfe (grün unterstrichen), also prüfe ich offenbar falsch.
Hinzu kommt, dass der gesuchte Schlüssel ein dword ist und keine Zeichenkette. Deswegen muss ich offenbar eine "doppelte Umwandlung" (so nenne ich das mal) vornehmen. Verstehe also nicht, was "(uint)(int)" genau machen.
Danke Abt. Du hast mir zwar jetzt den Weg Richtung Lösung gezeigt, aber warum das so ist habe ich trotzdem nicht verstanden.
Ich nutze doch nur eine Abkürzung um die exe aus zu führen und trotzdem ist ein Workingdir notwendig? Was passiert da im Hintergrund?
Ich habe mir ein kleines Tool geschrieben mit dem ich verschiedene cmd-Dateien als Administrator aufrufen kann.
Solange ich die exe aus dem gleichen Verzeichnis starte wo auch die cmd-Dateien liegen funktioniert es. Verwende ich aber die Verknüpfung zur exe werden die cmd-Dateien nicht mehr gefunden. Ich muss die cmd-Dateien dann in das gleiche Verzeichnis wie die Verknüpfung legen. Dann klappt es wieder.
Ich verstehe das nicht. Eine Verknüpfung ist doch nur ein Verweis auf die exe die gestartet wird. Was läuft falsch?
Ist WMI auch als Datenquelle zu sehen oder ist das was anderes? Für mich irgendwie schon, weil ich darüber ja die Informationen beziehe, die ich dann anzeigen lasse.
Ja, das mit der Abhängigkeit habe ich so gemeint, also Abhängigkeit von der Registry, die ja eine Systemkomponente ist. Ist für mich das gleiche. Ist es aber in der Sprache der Programmierung gar nicht und ich vermische etwas? Von WMI als Komponente bin ich ja auch abhängig. Wenn Microsoft da was ändert könnte mein Tool plötzlich andere Informationen ausgeben oder auch gar nicht mehr funktionieren. Ich frage das nur, um mich zukünftig hoffentlich verständlich aus zu drücken.
Die Frage nach "gutem" Code, war so gemeint, ob das Code ist mit dem man durchaus was anfangen kann. Bei dem alten "Spaghetti-Code" den ich an den Tag gelegt habe sind wir uns ja einig, dass man es drehen und wenden kann wie man will: Er funktionierte einfach nur, machte aber schon mein kleines Mini-Tool wahnsinnig unübersichtlich, weil alles in eine einzige Datei geklatscht wurde (Form1.cs) und am Ende über 1.000 Zeilen Code stand. Das war nicht gut.
Ich habe mir das durchgelesen. Bei dem Thema seid ihr ja echt total unterschiedlicher Meinungen.
Für mich als jemand der ohnehin schon stark zu kämpfen hat und ein Fall von "Unbedingt Wollen, aber eigentlich nicht Können" ist, ist das schwer, wenn die beiden aktivsten und hilfsbereitesten Mitglieder jeweils unterschiedliche Meinungen vertreten und man nun nicht so recht weis, was OK ist und was nicht.
Ich nehme jetzt mal mit, dass man die Methodenvarianten in klein gehaltenen Projekten verwenden kann und nicht grundlegend falsch ist, es aber durchaus sinnvoll ist bei größeren Projekten auf die Klassenvariante zu wechseln, bzw. zumindest dann, wenn man sich von einer Drittkomponente (Hier Registry) abhängig macht. Mit hoffentlich fortschreitendem Wissen sollte man dann beide Varianten beherrschen.
Zu definieren wäre dann noch, was als "Drittkomponente" zu definieren ist, weil im Grunde greift man bei WMI-Abfragen ja auch auf eine Drittkomponente (WMI-Klasse) zurück und ist von dieser abhängig.
@Th69: Abt schreibt noch, dass die Methodenvariante (in meiner Vorgehensweise?) das Drei-Schichten-Modell verletzt. Gehst du da mit? Wie handhabst du diese Thematik?
Ich habe das nun probiert, aber leider funktioniert das nicht. Ich habe den kompletten Code in eine eigene Klasse und am ende sogar in ein neues .net Framework 4.8 Projekt kopiert, weil "using Microsoft.WindowsAPICodePack.Resources;" in Version 7 des "Windows-API-CodePack-NET" nicht mehr unterstützt wird und ich dachte es liegt vielleicht daran. Habe deswegen Version 1.1.2 verwendet (das aber nicht .net Core 6 unterstützt) Daran lag es aber am ende nicht.
Fehler ist:
Zitat
'Der Zugriff auf "PowerManagementNativeMethods" ist aufgrund des Schutzgrads nicht möglich.
Die dekompilierte Datei, bzw. Methode von MessageManager.cs beginnt dann auch mit
internal static class MessageManager
{
}
und logischerweise kann man das nicht mal eben testweise auf public ändern.
Jetzt bin ich überfordert.
Btw: Die Demo-App, basierend auf WPF funktioniert auch nicht. Die lässt sich zwar kompilieren, wirft beim Starten dann aber auch den Fehler im Ereignisprotokoll.
Ebenfalls interessant: Die Methode (GetSystemEnergyState()) aus meiner App die letztens auf meinem Rechner zu Hause noch funktionierte, funktioniert nun nicht mehr, hat dafür aber auf einem meiner Geschäftsrechner wo sie vorher nicht funktionierte plötzlich funktioniert. Muss man das verstehen?
Also um das richtig zu verstehen: Für ein Mini-Tool ist der Code "OK" (Im Sinne von "Kann man so machen und ist nicht falsch"). Nur der Exception-Block ist Müll.
Für größere Projekte ist der Ansatz aber nicht gut und ich sollte auf die von dir vorgeschlagene Variante umschwenken und noch mehr lernen.
Danke Abt. Ich muss den Code jetzt erstmal nachvollziehen und verstehen. Geht noch über meien Wissensstand hinaus. Aber jetzt weiß ich in welche Richtung es gehen soll.
Ich habe mich beim coden an diesem Thread orientiert und mich für die Methodenlösung entschieden. War das falsch?
Ich habe den Code übrigens erstmal nicht in Code-Reviews gepostet, weil ich dachte, dass er gegen Regel 1 verstößt, also direkt kompilierbar sein muss. Weils aber nur eine Methode ist dachte ich, dass es hier falsch ist. Sorry.
Danke.
Ich habe das Tool gerade bei meiner zweiten Arbeitsstelle getestet und habe hier das gleiche Problem. Beides Dell-Rechner.
In der einen Arbeitsstelle ist es ein Optiplex 5040 und bei der anderen ein Optiplex 7050. Mittels Dell Command | Update (Sucht automatisch die aktuellen Treiber zum jeweiligen Dell-System) wurde sichergestellt, dass alles auf dem aktuellen Stand ist.
Die Ereignisanzeige sagt folgendes:
Zitat
Name der fehlerhaften Anwendung: SysteminventoryV2.exe, Version: 1.0.0.0, Zeitstempel: 0x6377dc84
Name des fehlerhaften Moduls: ntdll.dll, Version: 10.0.19041.2130, Zeitstempel: 0xb5ced1c6
Ausnahmecode: 0xc0000374
Fehleroffset: 0x00000000000ff6a9
ID des fehlerhaften Prozesses: 0x3c48
Startzeit der fehlerhaften Anwendung: 0x01d9210924c48852
Pfad der fehlerhaften Anwendung: [..]\SysteminventoryV2\bin\Debug\net6.0-windows\SysteminventoryV2.exe
Pfad des fehlerhaften Moduls: C:\WINDOWS\SYSTEM32\ntdll.dll
Berichtskennung: [...]
Vollständiger Name des fehlerhaften Pakets: <Hier steht nichts>
Anwendungs-ID, die relativ zum fehlerhaften Paket ist:
Ich kann das bis Projekt das bislang gemacht wurde gerne hier reinstellen falls euch das helfen sollte mir zu helfen.
Mein C# Programm funktioniert auf meinem Rechner zu Hause, aber im Geschäft wird die Anwendung in VS mit folgendem Fehler beendet:
Zitat
Das Programm "[16224] SysteminventoryV2.exe" wurde mit Code 3221226356 (0xc0000374) beendet.
Ursächlich dafür ist folgender Code:
// Use WindowsAPICodePack.
public static string GetSystemEnergyState()
{
string SystemEnergyState = PowerManager.PowerPersonality.ToString();
return SystemEnergyState;
}
lblSystemEnergyState.Text = SystemClass.GetSystemEnergyState(); // Wenn auskommentiert startet das Programm, bzw. bleibt offen
Wenn man Google bemüht finden sich Fehler in Bezug auf C++, zu C# eher weniger und nichts was mir weiterhelfen würde. Rechner wurde bereits neu gestartet. Fehler 0xc0000374 sieht für mich nach einem speicherproblem aus, aber stimmt das überhaupt und wenn ja, wieso?
Einmal mehr ein Dankeschön für dich.
Das löst mein aktuelles Problem, aber wie / wo kann ich nachschlagen, was mir das Pack noch alles so bietet und wie ich es anwende? Ich schätze nicht für alles gibt es eine Demoanwendung? Wie geht man mit Nu-Get Paketen um zu denen sich keine (ausreichende) Dokus finden, bzw. schlecht dokumentiert sind? Ist ja eine ganz allgemeine Sache.
Sorry, wollte nur das ironisch aufgreifen, was man in diversen Foren (so wortwörtlich hier nicht, das stimmt) so unsanft gerne an den Kopf geworfen bekommt.
Sollte echt nicht frustriert klingen und war auch nicht frustriert gemeint. Sollte zum Ausdruck bringen, dass ich mir bereits echt Mühe gegeben habe eine Dokumentation zu finden, zu lesen und zu verstehen.
Ich suche die Dokumentation zum Windows-API-Code-Pack. Ich würde gerne nachlesen, ob es dort eine Funktion gibt, um das Energieschema von Windows auszulesen. Scheinbar gibt es da nämlich was: https://stackoverflow.com/a/7457150
Im Git-Repository gibt es zwar unter anderem eine Demo-App unter source/Samples/PowerMgmtDemo/CS/, aber mit der kann ich nichts anfangen und in der Hilfedatei unter documentation/Windows API Code Pack Help.chm gibt es zwar ein Inhaltsverzeichnis, aber wenn man sich da durch klickt erscheint im rechten Fenster kein Inhalt.
Generell soll das API-Code-Pack mächtig und hilfreich sein, so dass sich das wohl echt lohnt die Funktionen da drin zu kennen und anwenden zu können.
Vielleicht noch als Randnotiz:
Ich habe bereits funktionierenden Code, aber der funktioniert nur, wenn die Anwendung explizit als Administrator aufgerufen wird. (Mit Adminrechten am System angemeldet zu sein reicht nicht. Gbit eine Exception)
public static string GetSystemEnergySettingState()
{
var searcher = new ManagementObjectSearcher(@"\\.\root\cimv2\power", "SELECT * FROM Win32_PowerPlan WHERE IsActive = True");
using ManagementObjectCollection managementObjectCollection = searcher.Get();
ManagementObject managementObject = managementObjectCollection.OfType<ManagementObject>().First();
string SystemEnergySettingState = managementObject["ElementName"].ToString() ?? "Default";
return SystemEnergySettingState;
}
Das gleiche Problem hatte ich mal, als ich auslesen wollte, ob Bitlocker aktiviert ist oder nicht. Damals bin ich dann auf den verlinkten Code, basierend auf dem Windows-API-Code-Pack-1.1 gestoßen, den ich aber nur kopiert habe. Wie hat der damalige Autor rausgefunden, wie er den Code schreiben muss? Wie geht man sowas an? https://stackoverflow.com/a/41310139
Danke. Das die Variablen außerhalb von if/else nicht mehr gilt war mir gar nicht mehr bewusst. Ich dachte das ist nur bei Methoden und Klassen der Fall.
Ja, das die Eigenschaften gar keinen Verweis mehr haben, habe ich heute im Geschäft gesehen. Dort hatte ich wohl mal die Funktion aktiviert um das zu sehen. Zu Hause hatte ich das noch nicht aktiviert (Jetzt schon :D)
Ich habe mich letztlich für die Methodenvariante entschieden, einzig weil sie mir besser gefällt. Ob und in welchen praktischen Anwendungsfällen die Klasse besser geeignet ist, habe ich nicht verstanden, aber da das Programm nur Daten ermitteln und übertragen soll und anschließend beendet wird ist es wohl ohnehin egal.
Ich habe da ein Verständnisproblem.
Folgender Code wirft Fehler:
Zitat
Fehler CS1023 Eine eingebettete Anweisung kann keine Deklaration und keine Anweisung mit Bezeichnung sein.
public string? SystemBitlockerState { get; private set; }
public static string GetSystemBitlockerState()
{
IShellProperty prop = ShellObject.FromParsingName("C:").Properties.GetProperty("System.Volume.BitLockerProtection");
int? bitLockerProtectionStatus = (prop as ShellProperty<int?>)?.Value;
if (bitLockerProtectionStatus.HasValue && (bitLockerProtectionStatus == 1 || bitLockerProtectionStatus == 3 || bitLockerProtectionStatus == 5))
string SystemBitlockerState = "Aktiviert"; // Fehler hier
else
string SystemBitlockerState = "Deaktiviert"; // Fehler hier
return SystemBitlockerState; // Fehler hier
}
// // Lässt man das "string" weg gibt es den Fehler "Fehler CS0120 Für das nicht statische Feld, die Methode oder die Eigenschaft "SystemClass.SystemBitlockerState" ist ein Objektverweis erforderlich."
Ich verstehe den Unterschied nicht? Warum muss ich SystemBitlockerState in dieser Methode erste deklarieren? Vor allem habe ich die Variable doch schon ganz oben (Zeile 1) deklariert?
Wenn ich beispielsweise diese Methode heranziehe, dann ist das nicht notwendig.
public string? SystemComputerName { get; private set; }
public static string GetSystemComputername()
{
var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_ComputerSystem");
using ManagementObjectCollection managementObjectCollection = searcher.Get();
ManagementObject? managementObject = managementObjectCollection.OfType<ManagementObject>().First();
string SystemComputerName = managementObject["Name"].ToString() ?? "Default";
return SystemComputerName;
}
OK, codetechnische Umsetzung habe ich kapiert. (Hat etwas gedauert)
Die Frage bleibt aber, wann welche Vorgehensweise sinnvoller ist und ob meine Aufteilung sinnvoll ist.
Um es zu erklären.
In der Datei SystemClass.cs frage ich folgendes ab:
Zitat
public string? SystemComputerName { get; private set; }
public string? SystemOs { get; private set; }
public string? SystemLastStart { get; private set; }
public string? SystemOsCaption { get; private set; }
public string? SystemOsDisplayVersion { get; private set; }
public string? SystemLanIpAdress { get; private set; }
public string? SystemDhcp { get; private set; }
public string? SystemLastWindowsUpdate { get; private set; }
public string? SystemOtherMicrosoftUpdates { get; private set; }
public string? SystemSpaceCFree { get; private set; }
public string? SystemBitlocker { get; private set; }
public string? SystemBitockerKey { get; private set; }
public string? SystemEnergySetting { get; private set; }
public string? SystemWlanSsid { get; private set; }
public string? SystemLastUser { get; private set; }
Daneben wird es aber noch andere Dateien geben, beispielsweise eine HardwareClass.cs und dort frage ich dann beispielsweise sowas hier ab:
Zitat
public string? HardwareLocation { get; private set; }
public string? HardwareManufactor { get; private set; }
public string? HardwareModel { get; private set; }
public string? HardwareType { get; private set; }
public string? HardwareSerial { get; private set; }
public string? HardwareBios { get; private set; }
public string? HardwareCpu { get; private set; }
public string? HardwareRamCapacity { get; private set; }
public int? HardwareRamSlotsUsed { get; private set; }
public int? HardwareRamSlotsAvailable { get; private set; }
public string? HardwareMac { get; private set; }
public string? HardwareDrive { get; private set; }
public string? HardwareSpaceCAvailable { get; private set; }
public string? HardwareBatteryCapacity { get; private set; }
ich habe die codetechnische Umsetzung leider nicht ganz verstanden.
Kannst du das anhand eines Beispiels bitte deutlicher machen? Und was ist in welcher Situation der bessere Ansatz?
Wie meinst du das mit dem Assembly? Das scheint sehr interessant zu sein. Vielleicht macht es Sinn auf diesem Ansatz auf zu bauen, weil dann könnte man das ja in anderen Projekten direkt wiederverwenden, oder?
Was die Wiederverwertbarkeit angeht brauche ich diesen Codefetzen nochmal:
var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_ComputerSystem");
using ManagementObjectCollection managementObjectCollection = searcher.Get();
ManagementObject? managementObject = managementObjectCollection.OfType<ManagementObject>().First();
Damit kann ich dann zum Beispiel Hersteller und Typ ermitteln
Wo du aber das Thema "Design" ansprichst. Momentan sieht das Ganze noch so aus. Ich denke das ist nicht ganz richtig. Wenn das Programm mal Formen annimmt werden folgende Bestandteile enthalten sein:
- Systeminformationen ermitteln
- Abfragen einer Datenbank um diverse Informationen von Extern in das Programm zu laden
- Übertragung der Daten in eine MySQL-Datenbank
- Anbindung über eine (Web?)-API
So, mit der Angelegenheit befasst und zumindest das Ergebnis stimmt. Aber ist die Herangehensweise so auch richtig oder gibt es Einwände eurerseits?
Ich bin leider unsicher, bitte seht es mir nach, wenn ich öfters mal nachfrage. Ich denke aber es ist besser als es falsch verstanden zu haben und dann wieder umlernen zu müssen.
SystemClass.cs
namespace SysteminventoryV2
{
class SystemClass
{
// Deklaration
// System
public string? SystemComputerName { get; set; }
public string GetSystemComputername()
{
var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_ComputerSystem");
using ManagementObjectCollection managementObjectCollection = searcher.Get();
ManagementObject? managementObject = managementObjectCollection.OfType<ManagementObject>().First();
this.SystemComputerName = managementObject["Name"].ToString()?.ToLower() ?? "Default";
return SystemComputerName;
}
public SystemClass() // Irgendwie bissel doof, dass das so leer jetzt ist, aber löschen kann man das ja scheinbar auch nicht.
{
}
}
}
Form1.cs
[...]
private void Form1_Load(object sender, EventArgs e)
{
SystemClass test = new SystemClass();
lblSystemComputerName.Text = test.GetSystemComputername();
}
Hmmm, eventuell mache ich gerade den zweiten Schritt vor dem ersten. Wie dem aber auch sei: Ich komme nicht weiter. Ich denke es ist mal wieder eine Kleinigkeit:
Es spielt dabei überhaupt keine Rolle, ob ich die Methode mit oder ohne Überlasung anlage / aufrufe und ob mit oder ohne "return;" Das Ergebnis ist immer gleich.
Also ich habe folgenden beispielhaften Code:
Erstmal die Klasse. Diese ist in eine eigene Klassendateiausgelagert.
SystemClass.cs
namespace SysteminventoryV2
{
class SystemClass
{
// Deklaration
// System
string? systemComputerName;
// Methode
public SystemClass(string systemComputerName) // Egal ob mit oder ohne Paramter
{
var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_ComputerSystem");
using ManagementObjectCollection managementObjectCollection = searcher.Get();
ManagementObject? managementObject = managementObjectCollection.OfType<ManagementObject>().First();
this.systemComputerName = managementObject["Name"].ToString()?.ToLower() ?? "Default";
return; // Egal, ob mit oder ohne. Ergebnis immer gleich
}
}
Form1.cs
namespace SysteminventoryV2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
SystemClass test = new SystemClass("Bla"); // Wenn in der Methode kein Parameter, dann lasse ich das natürlich hier leer)
lblSystemComputerName.Text = test.ToString();
}
}
}
So, dass nun nicht der Name des Computers steht ist mit dem Code ja erstmal logisch. Ich hätte aber zumindest mal erwartet, dass bei "lblSystemComputerName" das Wort "Bla" steht. Stattdessen steht da aber "SysteminventoryV2.SystemClass". Warum? Was läuft falsch?
Ich habe den Code auch immer wieder versucht anzupassen und natürlich mittels Variable an den Computernamen zu kommen, aber von Variablen will der Methodenaufruf überhaupt nichts wissen und gibt nur Fehler zurück.
Könnt ihr mir da bitte weiterhelfen?
Direkt auch die nächste Frage hinterher: Ist der Codeaufbau so überhaupt richtig oder gehe ich hier schon direkt einen falschen Weg? Wie sähe er richtig aus? Am Ende möchte ich ein Programm haben mit dem ich mir Informationen über den Computer anzeigen lassen kann.