Visual Studio ist eine von Microsoft entwickelte integrierte Entwicklungsumgebung (IDE), die mehrere Programmiersprachen wie C++, C#, VB.NET, Python, JavaScript usw. unterstützt. Geeignet für die Entwicklung von Desktop-Anwendungen, Websites, Cloud-Diensten und mobilen Anwendungen.
Visual Studio unterstützt Windows-Betriebssysteme und Visual Studio für Mac ist für macOS konzipiert.
[*.cs] indent_style = space indent_size = 43. Nachdem die Datei gespeichert wurde, folgt die Formatierung automatisch diesen Regeln.
Sie können die auf Ihrem Computer installierte .NET SDK-Version über das Befehlszeilentool überprüfen:
dotnet --list-sdks
Wenn ein Ergebnis ähnlich dem folgenden angezeigt wird, weist dies auf die installierte SDK-Version hin:
8.0.100 [C:\Program Files\dotnet\sdk] 9.0.100-preview.3.24172.9 [C:\Program Files\dotnet\sdk]
Wenn die Version, die Sie verwenden möchten, nicht angezeigt wird, bedeutet dies, dass sie noch nicht installiert ist.
dotnetBefehlWenn diese Option nicht angezeigt wird, können Sie auch im Windows-Startmenü nach „Entwickler-Eingabeaufforderung für VS“ suchen, um sie zu öffnen.
Ctrl + `dotnetBefehl zum Überprüfen oder Betreiben des SDKVerwenden des Systems;
Klassenprogramm {
static void Main() {
string name = "world";
Console.WriteLine($"Hallo, {name}!");
}
}
(x, y) => x + yvar q = list.Where(x => x > 10);await Task.Delay(1000);CS8618 bedeutet: am Ende des Konstruktors,Nicht nullbarDie Eigenschaften von sind nicht initialisiert, daher warnt der C#-Compiler, dass dies der Fall sein könntenull。
öffentliche Klasse Produkt {
öffentlicher String Name { get; Satz; } // Warnung CS8618
}
public class Product {
public string Name { get; set; }
public Product(string name) {
Name = name;
}
}
public class Product {
public string Name { get; set; } = string.Empty;
}
public class Product {
public string? Name { get; set; }
}
→ Gilt fürNamevernünftigerweise erlaubt seinnullSituation.
requiredModifikatoren (unterstützt von C# 11+)öffentliche Klasse Produkt {
öffentliche erforderliche Zeichenfolge Name { get; Satz; }
}
// Der Aufrufer muss initialisiert werden
var p = neues Produkt { Name = „Mobiltelefon“ }; // OK
null:verwendenKonstrukteuroderrequired。null:Ändern zustring?。string.Empty)。floatUndSystem::StringAddieren Sie zum Beispiel:
System::String^ tmpStr = "Value: " + someFloat;
WannsomeFloatWenn es sehr groß oder sehr klein ist, wird es möglicherweise automatisch in wissenschaftlicher Notation angezeigt (z. B. 1.23E+05).System::String::FormatoderToStringVerwenden Sie eine Formatzeichenfolge, um die Anzahl der Dezimalstellen anzugeben und die wissenschaftliche Notation zu vermeiden.Verwenden des Namespace-Systems;
Float-Wert = 123456,789f;
// Methode 1: String::Format
String^ tmpStr1 = String::Format("Value: {0:F2}", value); // F2 stellt 2 Nachkommastellen dar
Console::WriteLine(tmpStr1);
// Methode 2: ToString passendes Format
String^ tmpStr2 = "Wert: " + value.ToString("F2");
Console::WriteLine(tmpStr2);
//Methode 3: Mehr Ziffern
String^ tmpStr3 = "Wert: " + value.ToString("F6");
Console::WriteLine(tmpStr3);
F2: Festes Dezimalpunktformat, 2 NachkommastellenF6: Festes Dezimalpunktformat, 6 NachkommastellenF{Ziffern}In C++/CLI,gcnew System::String(stdStr.c_str())Willestd::string(normalerweiseANSI / UTF-8Kodierung) direkt in .NETSystem::String,UndSystem::StringDie Erwartung istUTF-16。
WennstdStrWenn es Nicht-ASCII-Zeichen enthält (z. B. Chinesisch), werden verstümmelte Zeichen angezeigt.
marshal_as(Anregung)Namespace muss in Anführungszeichen gesetzt werden:
#include <msclr/marshal_cppstd.h>
Verwenden des Namensraums msclr::interop;
std::string stdStr = "Chinesischer Test";
System::String^ netStr = Marshal_as<System::String^>(stdStr);
✅ Diese Methode kann UTF-8 / ANSI automatisch korrekt in .NET Unicode konvertieren.
---#include <codecvt>
#include <locale>
std::string utf8Str = u8"Chinesischer Test";
std::wstring wideStr = std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>{}.from_bytes(utf8Str);
System::String^ netStr = gcnew System::String(wideStr.c_str());
---
std::wstringWenn die C++-Seite ursprünglich breite Zeichenfolgen verwendet, verwenden Sie diese einfach direkt:
std::wstring wstr = L"Chinesischer Test";
System::String^ netStr = gcnew System::String(wstr.c_str());
---
std::stringDie Quelle ist UTF-8 (in modernen Systemen üblich) → Verwendungmarshal_asodercodecvt。gcnew String(stdStr.c_str()), es sei denn, die Zeichenfolge ist rein englisch.In .NET (Sprachen wie C++/CLI, C#, VB.NET usw.)List<T>ist eine generische Sammlungskategorie,
Kann jede Art von Daten (T) speichern und eine dynamische Größenänderungsfunktion bereitstellen.
es gehört dazuSystem::Collections::GenericNamensraum.
//Namespace importieren
Verwenden des Namespace-Systems;
unter Verwendung des Namensraums System::Collections::Generic;
int main()
{
// Eine Liste zum Speichern von int deklarieren
List<int>^ zahlen = gcnew List<int>();
// Elemente direkt während der Initialisierung hinzufügen
List<String^>^ name = gcnew List<String^>({ "Alice", "Bob", "Charlie" });
}
List<int>^ nums = gcnew List<int>();
//Neues Element hinzufügen
nums->Add(10);
nums->Add(20);
nums->Add(30);
//An angegebener Position einfügen
nums->Insert(1, 15); // 15 an Index 1 einfügen
//Angegebenen Wert entfernen
nums->Remove(20);
//Entferne den angegebenen Index
nums->RemoveAt(0);
// Elemente abrufen
int value = nums[1]; // Holen Sie sich das Element bei Index 1
// Überprüfen Sie, ob es enthält
if (nums->Contains(30))
Console::WriteLine("Gefunden 30");
// Alle Elemente löschen
nums->Clear();
// for-Schleife
for (int i = 0; i < nums->Count; i++)
{
Console::WriteLine("{0}th element: {1}", i, nums[i]);
}
// für jede Schleife
für jeden (int n in nums)
{
Console::WriteLine(n);
}
| Eigenschaften/Methoden | veranschaulichen |
|---|---|
Count | Aktuelle Anzahl der Elemente |
Capacity | Interne Kapazität (kann automatisch wachsen) |
Add(item) | ein Element hinzufügen |
AddRange(collection) | Alle Elemente einer anderen Sammlung zusammenführen |
Insert(index, item) | Element an angegebener Position einfügen |
Remove(item) | Entfernt den angegebenen Wert (erste Übereinstimmung gefunden) |
RemoveAt(index) | Element am angegebenen Index entfernen |
Clear() | Entfernen Sie alle Elemente |
Contains(item) | Prüfen Sie, ob der angegebene Wert enthalten ist |
IndexOf(item) | Gibt den Index des Elements zurück (-1, wenn es nicht existiert) |
Sort() | Sortierelemente (für vergleichbare Typen) |
Reverse() | Kehren Sie die Reihenfolge der Elemente um |
10 15 30
In .NET (Sprachen wie C++/CLI, C#, VB.NET usw.)Dictionary<TKey, TValue>ist eine generische Sammlung,
Wird zum Speichern von „Schlüssel-Wert-Paaren“ verwendet. Jeder Schlüssel muss eindeutig sein und der Wert muss wiederholbar sein.
es gehört dazuSystem::Collections::GenericNamensraum.
//Namespace importieren
Verwenden des Namespace-Systems;
unter Verwendung des Namensraums System::Collections::Generic;
int main()
{
//Erstelle ein Wörterbuch mit dem Schlüssel als int und dem Wert als String
Dictionary<int, String^>^ users = gcnew Dictionary<int, String^>();
// Direkte Initialisierung
Benutzer->Add(1, „Alice“);
Benutzer->Add(2, "Bob");
Benutzer->Add(3, „Charlie“);
0 zurückgeben;
}
Dictionary<String^, int>^ ages = gcnew Dictionary<String^, int>();
//Neues Element hinzufügen
ages->Add("Tom", 25);
ages->Add("Jerry", 30);
//Wert ändern (über Indexer)
alter["Tom"] = 26;
// Hinzufügen oder ändern (wenn der Schlüssel nicht vorhanden ist, wird er automatisch hinzugefügt)
alter["Spike"] = 40;
// Element löschen
ages->Remove("Jerry");
//Auf Elemente zugreifen
if (ages->ContainsKey("Tom"))
{
Console::WriteLine("Toms Alter ist {0}", ages["Tom"]);
}
// Versuchen Sie, den Wert zu erhalten (Ausnahmen vermeiden)
int Alter;
if (ages->TryGetValue("Spike", Alter))
{
Console::WriteLine("Spikes Alter ist {0}", age);
}
Dictionary<int, String^>^ users = gcnew Dictionary<int, String^>();
Benutzer->Add(1, „Alice“);
Benutzer->Add(2, "Bob");
Benutzer->Add(3, „Charlie“);
// KeyValuePair-Schleife verwenden
für jeden (KeyValuePair<int, String^> Eintrag in Benutzer)
{
Console::WriteLine("Key = {0}, Value = {1}", Entry.Key, Entry.Value);
}
//Nur Schlüssel erhalten
für jeden (int-Schlüssel in Benutzern->Schlüssel)
{
Console::WriteLine("Key: {0}", key);
}
//Nur Werte abrufen
für jeden (String^ Name in Benutzern->Werte)
{
Console::WriteLine("Name: {0}", name);
}
| Eigenschaften/Methoden | veranschaulichen |
|---|---|
Add(key, value) | Fügen Sie einen neuen Satz von Schlüssel-Wert-Paaren hinzu |
Remove(key) | Entfernt den angegebenen Schlüssel und seinen entsprechenden Wert |
Clear() | Löschen Sie alle Projekte |
ContainsKey(key) | Überprüfen Sie, ob der angegebene Schlüssel vorhanden ist |
ContainsValue(value) | Überprüfen Sie, ob der angegebene Wert vorhanden ist |
TryGetValue(key, out value) | Rufen Sie den Wert sicher ab, ohne eine Ausnahme auszulösen |
Count | Die aktuelle Anzahl der Elemente im Wörterbuch |
Keys | Gibt eine Sammlung aller Schlüssel zurück |
Values | Gibt eine Sammlung aller Werte zurück |
Schlüssel = 1, Wert = Alice Schlüssel = 2, Wert = Bob Schlüssel = 3, Wert = Charlie Tom ist 26 Jahre alt Spike ist 40 Jahre alt
In .NET C++ können Sie verwendenSystem::IONamespaces bieten Funktionen zum Bearbeiten von Dateien und Verzeichnissen.
Das Abrufen der neuesten Dateien im Verzeichnis kann durch Lesen aller Dateiinformationen und Vergleichen des letzten Änderungsdatums erreicht werden.
Hier ist ein vollständiges Beispiel, das zeigt, wie Sie die neueste Datei in einem angegebenen Verzeichnis abrufen:
#include „stdafx.h“
#include <iostream>
#include <cliext/vector>
#include <System.IO>
Verwenden des Namespace-Systems;
Verwenden des Namensraums System::IO;
Verwenden des Namensraums cliext;
int main()
{
Versuchen Sie es
{
//Verzeichnispfad angeben
String^directoryPath = "C:\\Ihr\\Verzeichnis\\Pfad";
// Überprüfen Sie, ob das Verzeichnis existiert
if (!Directory::Exists(directoryPath))
{
Console::WriteLine("Verzeichnis existiert nicht: {0}", Verzeichnispfad);
return -1;
}
// Alle Dateien im Verzeichnis abrufen
Array^ files = Directory::GetFiles(directoryPath);
// Wenn sich keine Dateien im Verzeichnis befinden
if (Dateien->Länge == 0)
{
Console::WriteLine("Es gibt keine Datei im Verzeichnis.");
0 zurückgeben;
}
// Finde die neueste Datei
String^ neuesteFile = nullptr;
DateTime neuesteTime = DateTime::MinValue;
für jede (String^ Datei in Dateien)
{
// Die letzte Änderungszeit der Datei abrufen
DateTime lastWriteTime = File::GetLastWriteTime(file);
// Zeit vergleichen und neueste Dateiinformationen aktualisieren
if (lastWriteTime > neuesteTime)
{
neuesteTime = lastWriteTime;
neuesteDatei = Datei;
}
}
// Die neuesten Dateiinformationen ausgeben
Console::WriteLine("Neueste Datei: {0}", neuesteDatei);
Console::WriteLine("Letzte Änderungszeit: {0}", neuesteZeit);
}
Catch (Ausnahme^ ex)
{
Console::WriteLine("Ein Fehler ist aufgetreten: {0}", ex->Message);
}
0 zurückgeben;
}
Directory::GetFilesMethode zum Abrufen aller Archivpfade im angegebenen Verzeichnis.File::GetLastWriteTimeMethode zum Abrufen der letzten Änderungszeit jeder Datei.try-catchDer Block fängt potenzielle Ausnahmen ab, z. B. ein Verzeichnis, das nicht existiert oder auf das nicht zugegriffen werden kann.System.ReflectionEs handelt sich um einen Namespace im .NET Framework, der Tools zum Überprüfen und Bearbeiten von Metadaten bereitstellt und es Entwicklern ermöglicht, Typen, Methoden, Eigenschaften usw. zur Laufzeit dynamisch zu prüfen und Objekte dynamisch zu erstellen und zu bearbeiten.
Assembly: Stellt eine geladene Assembly dar und stellt Methoden zum Laden, Erkunden und Reflektieren von Assemblys bereit.Type: Stellt einen Typ dar, einschließlich Kategorien, Strukturen, Schnittstellen usw., und bietet die Funktion zum Abrufen von Typinformationen.MethodInfo: Stellt Methodeninformationen dar und ermöglicht es Entwicklern, Methodeneigenschaften zu überprüfen und sie dynamisch aufzurufen.PropertyInfo: Stellt Attributinformationen dar und bietet Zugriff auf Attributmetadaten.FieldInfo: Stellt Feldinformationen dar und kann für den Zugriff auf Felder des Typs verwendet werden.Das Folgende ist eine VerwendungSystem.ReflectionEin Beispielprogramm, das zeigt, wie Typen und Methoden dynamisch überprüft und Methoden aufgerufen werden.
//Definieren Sie eine einfache Beispielklasse
öffentliche Klasse SampleClass {
öffentlicher String SayHello(Stringname) {
return $"Hallo, {name}!";
}
}
// Verwenden Sie Reflection, um Methoden dynamisch aufzurufen
Verwenden des Systems;
Verwenden von System.Reflection;
Klassenprogramm {
static void Main() {
//Eine Instanz der SampleClass-Klasse erstellen
Typ sampleType = typeof(SampleClass);
object sampleInstance = Activator.CreateInstance(sampleType);
// Informationen zur SayHello-Methode abrufen
MethodInfo methodInfo = sampleType.GetMethod("SayHello");
// Rufen Sie die SayHello-Methode dynamisch auf
object result = methodInfo.Invoke(sampleInstance, new object[] { "World" });
Console.WriteLine(result); // Ausgabe: Hallo Welt!
}
}
Im obigen Beispiel verwenden wirActivator.CreateInstanceum eine Instanz der Kategorie zu erstellen und zu verwendenMethodInfo.InvokeMethode aufrufenSayHello。
System::Management::ManagementClassIst eine der Klassen im .NET Framework, die die Arbeit mit Windows Management Instrumentation (WMI) ermöglicht. Es ermöglicht Entwicklern, Systeminformationen wie Hardware, Betriebssystem, Netzwerkeinstellungen usw. zu lesen, zu bearbeiten und zu verwalten.
System::ManagementbefindetSystem.Management.dllIm Namespace müssen Sie eine Referenz hinzufügen, bevor Sie diese Funktion verwenden können.
// C++/CLI-Schreibmethode
Verwenden des Namespace-Systems;
Verwenden des Namensraums System::Management;
int main() {
versuche es mit {
ManagementClass^ mc = gcnew ManagementClass("Win32_OperatingSystem");
für jedes (ManagementObject^ mo in mc->GetInstances()) {
Console::WriteLine("Betriebssystemname: {0}", mo["Caption"]);
}
}
Catch (Ausnahme^ ex) {
Console::WriteLine("Error: {0}", ex->Message);
}
0 zurückgeben;
}
System.Management.dllReferenz.ManagementException。ManagementObjectManagementObjectSearcherManagementBaseObjectnet stop winmgmt winmgmt /resetrepository net start winmgmt
Get-WmiObject Win32_NetworkAdapterConfigurationMethode 2: WMI-Testtool (wbemtest)
wbemtestroot\cimv2und verbindenSELECT * FROM Win32_NetworkAdapterConfigurationDISM /Online /Cleanup-Image /RestoreHealthSystemabbild reparierenIn .NET C++-Projekten (oder häufiger C++/CLI-Projekten) erfordert das Lesen und Parsen von HTML-Tabellen in Dateien normalerweise die Verwendung einer externen HTML-Parsing-Bibliothek, da das Standard-.NET-Framework und C++ selbst nicht über integrierte leistungsstarke HTML-DOM-Parsing-Funktionen verfügen. Eine gängige und effiziente Option besteht darin, eine Bibliothek in C# oder einer anderen .NET-Sprache zu verwenden und dann in C++/CLI darauf zu verweisen.
Für C++/CLI-Projekte ist die Verwendung der praktischste und empfehlenswerteste AnsatzHtml Agility Pack(HAP), ein sehr beliebter .NET-HTML-Parser. Obwohl HAP in C# geschrieben ist, kann es nahtlos als Referenz in jeder .NET-Sprache, einschließlich C++/CLI, verwendet werden.
HtmlAgilityPack。Das Folgende ist ein C++/CLI-Codeausschnitt, der zeigt, wie man eine lokale HTML-Datei liest und die erste Tabelle darin analysiert (<table>) Inhalt:
#using <System.dll>
#using <System.Xml.dll>
#using <HtmlAgilityPack.dll> // Stellen Sie sicher, dass die Referenz enthalten ist
Verwenden des Namespace-Systems;
Verwenden des Namensraums System::IO;
Verwenden des Namensraums HtmlAgilityPack;
void ParseHtmlTable(String^ filePath)
{
// Überprüfen Sie, ob die Datei existiert
if (!File::Exists(filePath))
{
Console::WriteLine("Fehler: Datei existiert nicht.");
zurückkehren;
}
// 1. HTML-Datei laden
HtmlDocument^ doc = gcnew HtmlDocument();
Versuchen Sie es
{
//HTML aus Datei laden
doc->Load(filePath);
}
Catch (Ausnahme^ ex)
{
Console::WriteLine("Beim Laden der Datei ist ein Fehler aufgetreten: " + ex->Message);
zurückkehren;
}
// 2. Wählen Sie die erste <Tabelle> Knoten
HtmlNode^ table = doc->DocumentNode->SelectSingleNode("//table");
if (Tabelle != nullptr)
{
Console::WriteLine("Suchen Sie die HTML-Tabelle und beginnen Sie mit dem Parsen...");
// 3. Alles auswählen <tr> (Tabellenspalte) Knoten
HtmlNodeCollection^ rows = table->SelectNodes(".//tr");
if (Zeilen != nullptr)
{
für jede (HtmlNode^ Zeile in Zeilen)
{
// 4. Wählen Sie <td> oder <th> (Zellen-)Knoten
// Verwenden Sie | Operator zur Auswahl von <td> oder <th>
HtmlNodeCollection^ celles = row->SelectNodes("td | th");
if (Zellen != nullptr)
{
String^ rowData = "";
für jede (HtmlNode^ Zelle in Zellen)
{
// Den internen Text der Zelle abrufen und die führenden und nachfolgenden Leerzeichen entfernen
rowData += cell->InnerText->Trim() + "\t";
}
Console::WriteLine(rowData);
}
}
}
sonst
{
Console::WriteLine("<tr> Tag nicht in Tabelle gefunden.");
}
}
sonst
{
Console::WriteLine("<table>-Tag nicht in der Datei gefunden.");
}
}
int main(array<String^>^ args)
{
// Bitte ersetzen Sie „your_html_file.html“ durch Ihren tatsächlichen Dateipfad
String^ htmlFilePath = "C:\\path\\to\\your_html_file.html";
ParseHtmlTable(htmlFilePath);
0 zurückgeben;
}
doc->Load(filePath): Lokale HTML-Dateien in die DOM-Struktur von HAP einlesen.SelectSingleNode("//table"): Verwenden Sie den XPath-Ausdruck, um den ersten in der Datei auszuwählen<table>Element.SelectNodes(".//tr"): Wählen Sie den aktuellen Knoten aus (<table>) alles unten<tr>Element.SelectNodes("td | th"):Wählen<tr>Alles unter dem Knoten<td>oder<th>Element.cell->InnerText: Rufen Sie den Klartext des Zelleninhalts ab und HTML-Tags werden automatisch entfernt.gcnewUnd^:Dies ist die Syntax, die von C++/CLI zum Erstellen und Verwalten von .NET-Objekten verwendet wird (verwaltet durch den CLR-Garbage-Collection-Mechanismus).HttpListenerMit der Klasse können Sie einen einfachen, selbstgehosteten HTTP-Server in einer C++/CLI-Anwendung erstellen.
Erstellen Sie ein C++/CLI-Projekt in Visual Studio (z. B. CLR-Konsolenanwendung) und stellen Sie sicher, dass darauf verwiesen wirdSystem.dll。
Folgendes wird verwendetHttpListenerC++/CLI-Code zum Erstellen eines einfachen API-Servers und zur Verarbeitung von GET-Anfragen.
#using <System.dll>
Verwenden des Namespace-Systems;
Verwenden des Namensraums System::Net;
Verwenden des Namensraums System::Threading::Tasks;
Verwenden des Namensraums System::Text;
// Funktion zur Verarbeitung eingehender HTTP-Anfragen
void HandleRequest(HttpListenerContext^ Kontext)
{
HttpListenerRequest^ request = context->Request;
HttpListenerResponse^ Response = context->Response;
//Standardantworteinstellungen
Response->ContentType = "application/json; charset=utf-8";
String^ ResponseString = "";
Antwort->StatusCode = 200; // Standard ist OK
// Überprüfen Sie den angeforderten Pfad und die angeforderte Methode
String^ url = request->RawUrl->ToLower();
if (url == "/api/status" && request->HttpMethod == "GET")
{
// GET /api/status-Anfrage verarbeiten
ResponseString = "{\"Status\": \"Server läuft\", \"Sprache\": \"C++/CLI\"}";
}
sonst
{
// Andere undefinierte Anfragen bearbeiten
Antwort->StatusCode = 404; // Nicht gefunden
ResponseString = "{\"error\": \"404 Not Found\", \"path\": \"" + url + "\"}";
}
// String-Antwort in Bytes konvertieren und in den Ausgabestream schreiben
array<Byte>^ buffer = Encoding::UTF8->GetBytes(responseString);
Antwort->ContentLength64 = buffer->Length;
Versuchen Sie es
{
Antwort->OutputStream->Write(buffer, 0, buffer->Length);
Antwort->OutputStream->Close();
}
Catch (Ausnahme^)
{
// Schreibfehler ignorieren, normalerweise trennt der Client die Verbindung
}
}
// Funktion zum Starten von HttpListener
void StartListener(String^ Präfix)
{
HttpListener^ listener = gcnew HttpListener();
Versuchen Sie es
{
listener->Präfixe->Add(prefix);
listener->Start();
Console::WriteLine("C++/CLI API Server gestartet, überwacht: {0}", Präfix);
}
Catch (Ausnahme^ ex)
{
Console::WriteLine("Beim Starten von HttpListener ist ein Fehler aufgetreten. Bitte bestätigen Sie, ob die Berechtigung oder Adresse belegt ist.");
Console::WriteLine("Fehlermeldung: {0}", ex->Message);
zurückkehren;
}
// Asynchrone Schleife, weiterhin Anfragen empfangen
while (listener->IsListening)
{
Versuchen Sie es
{
// Synchron auf die nächste Anfrage warten
HttpListenerContext^ context = listener->GetContext();
// Verwenden Sie Task, um Anforderungen asynchron zu verarbeiten, um das Blockieren von Abhörschleifen zu vermeiden
Task::Factory->StartNew(gcnew Action<HttpListenerContext^>(&HandleRequest), context);
}
Catch (Ausnahme^)
{
// Eine Ausnahme wird ausgelöst, wenn der Listener gestoppt wird
Pause;
}
}
if (listener->IsListening)
{
listener->Close();
}
}
int main(array<String^>^ args)
{
//Legen Sie das URL-Präfix für die Überwachung fest
// Hinweis: Unter Windows sind möglicherweise Administratorrechte oder die Verwendung von netsh zum Registrieren des URL-Namespace erforderlich.
String^ listenPrefix = "http://localhost:8080/";
StartListener(listeningPrefix);
Console::WriteLine("Drücken Sie eine beliebige Taste, um den Server zu beenden...");
Console::ReadKey(true);
0 zurückgeben;
}
Wenn der Code auf Windows-Systemen nicht mit Administratorrechten ausgeführt wird,HttpListenerBeim Abhören eines bestimmten Ports kann eine Ausnahme „Zugriff verweigert“ ausgelöst werden. Mit dem folgenden Befehl können Sie einen URL-Namespace registrieren, um Nicht-Administratorkonten die Ausführung des Servers zu ermöglichen:
netsh http add urlacl url=http://+:8080/ user=Everyone
Inhttp://+:8080/sollte durch das tatsächlich in Ihrem Code verwendete Präfix ersetzt werden.
In ASP.NET Core ist der Standardprojekttyp zum Erstellen eines HTTP-API-Servers die Web-API. Neueste .NET-Version (.NET 6 und höher) empfohlenMinimal APIsum schnell und einfach API-Endpunkte zu erstellen.
Minimale APIs kombinieren Startlogik (Startup) und Routing-Definitionen (Routing) in einer einzigen Datei mit minimalen Dateien und CodezeilenProgram.csMitte.
Um ein neues Minimal-API-Projekt zu erstellen, können Sie die .NET CLI (Command Line Interface) verwenden:
dotnet new web -n MinimalApiServer
cd MinimalApiServer
Das Folgende ist die Kerndatei des Minimal API-ServersProgram.csVollständiger Inhalt. Es definiert den Serverstart, die Middleware und zwei API-Endpunkte (einen GET, einen POST).
// 1. Erstellen Sie eine WebApplication-Instanz (ersetzt die traditionelle Startup-Kategorie)
var builder = WebApplication.CreateBuilder(args);
// 2. Dienst registrieren (Dienstkonfiguration)
//Hier können Sie Datenbankverbindungen, Verifizierungsdienste usw. hinzufügen.
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(); // Swagger/OpenAPI-Unterstützung aktivieren
var app = builder.Build();
// 3. Konfigurieren Sie die HTTP-Anforderungspipeline (Middleware-Konfiguration)
// Aktivieren Sie in der Entwicklungsumgebung die Swagger-Benutzeroberfläche zum Testen
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
// HTTPS-Umleitung aktivieren (empfohlen für Produktionsumgebungen)
// app.UseHttpsRedirection();
// 4. API-Endpunkt definieren (Endpunktdefinition)
// Entitätsklasse, die für POST-Anfragen verwendet wird
Public Record Product(int Id, string Name, decimal Price);
// GET-Anforderungsendpunkt: /api/hello
app.MapGet("/api/hello", () =>
{
return Results.Ok(new { message = "Hallo von der ASP.NET Core Minimal API!", timestamp = DateTime.Now });
});
// GET-Anfrageendpunkt: /api/products/{id}
app.MapGet("/api/products/{id}", (int id) =>
{
// Angenommen, Produkte aus der Datenbank abzufragen
if (id == 1)
{
return Results.Ok(new Product(1, "laptop", 1200.00m));
}
return Results.NotFound(new { error = $"Produkt-ID {id} nicht gefunden" });
});
// POST-Anfrageendpunkt: /api/products
app.MapPost("/api/products", (Produktprodukt) =>
{
// ASP.NET Core deserialisiert den JSON-Anfragetext automatisch in eine Produktentität
Console.WriteLine($"Neues Produkt erhalten: {product.Name} (ID: {product.Id})");
//In der tatsächlichen Anwendung wird hier der Datenbank-Hinzufügungsvorgang ausgeführt.
//Return 201 Statuscode erstellt
return Results.Created($"/api/products/{product.Id}", product);
});
// 5. Starten Sie die Anwendung und beginnen Sie mit der Überwachung auf HTTP-Anfragen
app.Run();
dotnet run。http://localhost:5000oderhttps://localhost:7000laufen./api/hello (GET)。/swaggerPfad zur Anzeige der Swagger-Benutzeroberfläche zum Testen aller definierten API-Endpunkte.app.UseSwagger()、app.UseHttpsRedirection()usw. Diese Komponenten bilden nacheinander die Anforderungsverarbeitungspipeline. Jede Anfrage fließt durch diese Middlewares.app.MapPostWenn der Client im Beispiel JSON-Daten sendet, konvertiert (deserialisiert) ASP.NET Core diese automatisch in C#.ProductAufzeichnen.Die meisten modernen Chromebooks unterstützen die Ausführung von Linux-Anwendungen über das Crostini-Projekt.
Einstellungen > Erweitert > Entwickler > Linux aktivieren (Beta)。sudo apt update
sudo apt install -y dotnet-sdk-7.0
Mit der Cloud-Plattform können Sie .NET-Programme auf Ihrem Chromebook ausführen, ohne lokal Software installieren zu müssen.
Chromebooks unterstützen die Verwendung von Containern zum Ausführen von Anwendungen, und Sie können eine .NET-Umgebung über Docker starten.
docker pull mcr.microsoft.com/dotnet/runtime:7.0
Ab .NET 6 können Anwendungen auf mehreren Plattformen, einschließlich Linux, ausgeführt werden. Kompilieren Sie Ihre App in einem plattformübergreifenden Format und stellen Sie sie auf Chromebooks bereit.
dotnet publish -c Release -r linux-x64 --self-contained
Verwenden des Systems;
Verwenden von System.Windows.Forms;
öffentliche Klasse MyForm : Form {
public MyForm() {
Button btn = new Button();
btn.Text = "Klick mich";
btn.Click += (s, e) => MessageBox.Show("Sie haben auf die Schaltfläche geklickt!");
Controls.Add(btn);
}
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.Run(new MyForm());
}
}
// MainWindow.xaml
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Titel="WPF-Beispiel" Höhe="200" Breite="300">
<StackPanel>
<Button Content="Klick mich" Click="Button_Click"/>
</StackPanel>
</Fenster>
// MainWindow.xaml.cs
private void Button_Click(object sender, RoutedEventArgs e) {
MessageBox.Show("Sie haben auf die Schaltfläche geklickt!");
}
// MainPage.xaml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
x:Class="MyApp.MainPage">
<VerticalStackLayout Spacing="25" Padding="30">
<Button Text="Click me" Clicked="OnButtonClicked"/>
</VerticalStackLayout>
</ContentPage>
// MainPage.xaml.cs
private void OnButtonClicked(object sender, EventArgs e) {
wait DisplayAlert("Notification", "Sie haben auf die Schaltfläche geklickt!", "OK");
}
| Technologie | verwenden | Plattform |
|---|---|---|
| WinForms | Entwickeln Sie schnell Desktop-Anwendungen | Windows |
| WPF | Komplexe Desktop-Anwendungen, MVVM-Muster | Windows |
| MAUI | Plattformübergreifende Anwendung | Windows、macOS、Android、iOS |
| Blazor | Web-Frontend-Entwicklung | Plattformübergreifend (Web) |
Die folgenden Beispiele zeigen die VerwendungControls->GetEnumerator()Methode zum schrittweisen Durchlaufen aller untergeordneten Steuerelemente in einem .NET-Formular und zum stapelweisen Ändern ihrer Eigenschaften.
Verwenden des Systems;
Verwenden von System.Drawing;
Verwenden von System.Windows.Forms;
public class FormExample : Form
{
öffentliches FormExample()
{
//Initialize some controls
Button button1 = new Button { Text = "Button 1", Location = new Point(10, 10) };
TextBox textBox1 = new TextBox { Location = new Point(10, 50) };
Controls.Add(button1);
Controls.Add(textBox1);
// Verwenden Sie GetEnumerator(), um die Steuerelementeigenschaften zu durchlaufen und zu ändern
ModifyControls();
}
private void ModifyControls()
{
var enumerator = Controls.GetEnumerator();
while (enumerator.MoveNext())
{
Control control = (Control)enumerator.Current;
// Einstellungsbeispiel: Hintergrundfarbe aller Controls auf Hellblau setzen
control.BackColor = Color.LightBlue;
// Wenn das Steuerelement eine TextBox ist, machen Sie es nicht bearbeitbar
if (Steuerelement ist TextBox)
{
control.Enabled = false;
}
}
}
//Starten Sie die Anwendung
public static void Main()
{
Application.Run(new FormExample());
}
}
**GetEnumerator()**: Verwenden Sie die Methode „Controls.GetEnumerator()“, um den Enumerator des Steuerelements abzurufen, sodass alle untergeordneten Steuerelemente durchlaufen werden können.
**Bedingte Änderung**: Ändern Sie in der „while“-Schleife die Eigenschaften jedes „Control“-Objekts, z. B. indem Sie die Hintergrundfarbe auf Hellblau setzen, und nehmen Sie spezifische Änderungen basierend auf dem Steuerelementtyp vor, z. B. indem Sie die „TextBox“ auf nicht bearbeitbar setzen.
**Verwendung**: Diese Methode ist sehr effektiv, wenn Sie Eigenschaften stapelweise ändern müssen, z. B. das Anpassen des UI-Stils oder das Deaktivieren mehrerer Steuerelemente unter bestimmten Umständen.
Nach der Ausführung ändert sich die Hintergrundfarbe aller Steuerelemente in Hellblau und alle „TextBox“-Steuerelemente werden auf nicht bearbeitbar gesetzt.
In C++/CLI,MessageBox::ShowEs handelt sich um eine statische Methode zum Öffnen eines Dialogfelds. Die umfassendste Aufrufmethode umfasst: Nachrichteninhalt, Titel, Schaltflächentyp, Symbol und Standardschaltfläche.
Verwenden des Namensraums System::Windows::Forms;
//Die einfachste Anzeige
MessageBox::Show("Vorgang abgeschlossen");
//Vollständige Parameterversion
DialogResult result = MessageBox::Show(
„Möchten Sie diese Datei wirklich löschen?“, // Nachrichteninhalt (Text)
„Löschen bestätigen“, // Fenstertitel (Beschriftung)
MessageBoxButtons::YesNo, // Tastenkombination
MessageBoxIcon::Warning, // Eingabeaufforderungssymbol
MessageBoxDefaultButton::Button2 // Standardmäßig liegt der Fokus auf der zweiten Schaltfläche (Nein)
);
Muss aktiviert werden, wenn eine Schaltfläche mehrere Optionen enthält, z. B. Ja/Nein oder OK/AbbrechenDialogResultum die nachfolgende Logik zu bestimmen.
if (result == DialogResult::Yes) {
//Löschlogik ausführen
} sonst {
//Vorgang abbrechen
}
| Kategorie | Gemeinsame Optionen | veranschaulichen |
|---|---|---|
| MessageBoxButtons | OK, OKCancel, YesNo, YesNoCancel | Definiert, welche Schaltflächen unterhalb des Dialogfelds angezeigt werden. |
| MessageBoxIcon | Information, Warning, Error, Question | Definieren Sie die links angezeigten Symbol- und Systemsoundeffekte. |
| DialogResult | OK, Cancel, Yes, No | ShowDer Rückgabewert der Methode gibt an, welche Taste der Benutzer gedrückt hat. |
WeilMessageBox::ShowEmpfangen wird .NETSystem::String^Wenn Sie über native C++-Daten verfügen, müssen Sie diese konvertieren.
#include <string>
#include <msclr/marshal_cppstd.h>
// std::string in MessageBox anzeigen
std::string cpp_str = „Kernfehler“;
MessageBox::Show(msclr::interop::marshal_as<System::String^>(cpp_str));
//Float formatieren und anzeigen
float dist = 1,23456f;
MessageBox::Show("Der Abstand ist: " + dist.ToString("F2")); // Anzeige „Der Abstand beträgt: 1,23“
MessageBoxButtons::OK。using namespace System;Undusing namespace System::Windows::Forms;, möglicherweise mit anderenMessageBoxDefinitionskonflikt, es wird empfohlen, den vollständigen Namen zu verwendenSystem::Windows::Forms::MessageBox::Show(...)。System::Windows::Forms::MessageBoxist darauf ausgelegt, „Benachrichtigungen“ oder „Warnungen“ anzuzeigen und Benutzerentscheidungen über Standardschaltflächen einzuholen. Es verfügt nicht über die Funktion, Steuerelemente dynamisch einzufügen. Wenn Sie Text eingeben müssen, ist die Standardmethode die VererbungSystem::Windows::Forms::FormErstellen Sie ein benutzerdefiniertes Dialogfeld.
Dies ist eine einfache, wiederverwendbare Kategorie, die Beschriftungen, Textfelder und OK-Schaltflächen enthält.
Verwenden des Namespace-Systems;
Verwenden des Namensraums System::Windows::Forms;
Verwenden des Namensraums System::Drawing;
öffentliche Referenzklasse CustomInputBox: öffentliche Form {
öffentlich:
TextBox^ txtInput;
Schaltfläche^ btnOK;
Label^ lblPrompt;
CustomInputBox(String^ title, String^ prompt) {
//Grundlegende Eigenschaften des Formulars festlegen
this->Text = Titel;
this->Size = System::Drawing::Size(300, 150);
this->FormBorderStyle = System::Windows::Forms::FormBorderStyle::FixedDialog;
this->StartPosition = FormStartPosition::CenterParent;
this->MaximizeBox = false;
this->MinimizeBox = false;
// Eingabeaufforderungstext
lblPrompt = gcnew Label();
lblPrompt->Text = prompt;
lblPrompt->Location = Point(15, 15);
lblPrompt->Size = System::Drawing::Size(250, 20);
// Eingabefeld
txtInput = gcnew TextBox();
txtInput->Location = Point(15, 40);
txtInput->Size = System::Drawing::Size(250, 20);
// OK-Taste
btnOK = gcnew Button();
btnOK->Text = "OK";
btnOK->DialogResult = System::Windows::Forms::DialogResult::OK;
btnOK->Location = Point(190, 75);
// Steuerelemente hinzufügen
this->Controls->Add(lblPrompt);
this->Controls->Add(txtInput);
this->Controls->Add(btnOK);
//Legen Sie die Standardschaltfläche fest (das Drücken der Eingabetaste entspricht dem Klicken auf OK)
this->AcceptButton = btnOK;
}
};
verwendenShowDialog()Die Methode öffnet das Fenster im Forced-Response-Modus und prüft, ob der Rückgabewert vorhanden istOK。
void OnButtonClick() {
CustomInputBox^ ibox = gcnew CustomInputBox("Systemeingabe", "Bitte geben Sie den Dateinamen ein:");
if (ibox->ShowDialog() == System::Windows::Forms::DialogResult::OK) {
String^ userInput = ibox->txtInput->Text;
if (!String::IsNullOrWhiteSpace(userInput)) {
// Verarbeite die vom Benutzer eingegebene Zeichenfolge
MessageBox::Show("Sie haben Folgendes eingegeben: " + userInput);
}
}
}
| planen | Komplexität der Implementierung | Vorteil | Mangel |
|---|---|---|---|
| Benutzerdefiniertes Formular | Mitte | Volle Kontrolle über das Layout und die Möglichkeit, Validierungslogik hinzuzufügen (z. B. begrenzte Anzahl). | Es muss mehr Schnittstellencode geschrieben werden. |
| VB Interaction | Niedrig | Eine Codezeile abgeschlossen. | Muss zitierenMicrosoft.VisualBasic, der Stil ist festgelegt und kann nicht angepasst werden. |
| Win32 API | hoch | Keine .NET-Umgebung erforderlich. | Die Entwicklung in C++/CLI ist äußerst trivial und schwer zu warten. |
delete ibox;Oder stellen Sie sicher, dass Ressourcen nach Ablauf des Bereichs ordnungsgemäß freigegeben werden.ShowDialog()stattShow()Dadurch wird sichergestellt, dass der Benutzer das Eingabefenster schließen muss, bevor er das Hauptfenster bedient, und direkt abrufen kannDialogResult。PictureBox zeigt ein rotes Kreuz an, was darauf hinweist, dass dieOnPaintWährend der Ausführung ist eine Ausnahme aufgetreten.
Allerdings verschluckt .NET diese Ausnahme standardmäßig und zeigt sie nicht im allgemeinen Protokoll oder an den Unterbrechungspunkten an, was das Debuggen erschwert.
| Grund | veranschaulichen |
|---|---|
| Bitmap wurde entsorgt | PictureBox enthält weiterhin einen Verweis auf die Bitmap und auf die freigegebenen Ressourcen wird während Paint zugegriffen. |
| Der Quellstream ist geschlossen | Nachdem Sie Stream zum Erstellen einer Bitmap verwendet und den Stream dann geschlossen haben, werden die Bitmap-Daten ungültig. |
| Cross-Thread-Zugriff | Der Hintergrundthread ändert oder weist Bitmap direkt zu, was zu Konflikten mit dem UI-Thread führt. |
| Erschöpfung der GDI+-Ressourcen | Eine große Anzahl von Bitmaps wird nicht entsorgt, was zu unzureichendem GDI+-Speicher führt |
| Ausnahmemeldung | Grundursache | Lösungsrichtung |
|---|---|---|
| ArgumentException: Parameter is not valid | Bitmap wurde entsorgt oder Stream wurde geschlossen | Bitmap tief kopieren, nicht auf Stream angewiesen |
| InvalidOperationException: object is in use elsewhere | Greifen Sie Thread-übergreifend gleichzeitig auf dieselbe Bitmap zu | Fügen Sie eine Sperre hinzu oder rufen Sie den UI-Thread erneut auf |
| OutOfMemoryException | GDI+-Ressourcen sind erschöpft oder das Bildformat wird nicht unterstützt | Entsorgen Sie die alte Bitmap umgehend und überprüfen Sie die Bildquelle |
| ExternalException: A generic error occurred in GDI+ | Die der Bitmap entsprechende Datei oder der Stream existiert nicht mehr | Stellen Sie sicher, dass die Quelle während der Bitmap-Lebensdauer noch gültig ist |
Da das Problem des Roten Kreuzes möglicherweise nicht oft auftritt, wird empfohlen, in der folgenden Reihenfolge vorzugehen:
OnPaint,
Verwenden Sie try/catch, um Ausnahmen abzufangen und auszugebenDebug.WriteLine,
nach der Erfassung ausführenthis.Image = nullVermeiden Sie anhaltende rote Kreuze.Environment.StackTraceFinden Sie heraus, wer die Ressource zu welchem Zeitpunkt freigegeben hat.ImageAuf null setzen und dann entsorgen.Alle Bildwechselvorgänge werden in diesem Modus ausgeführt, um die Thread-Sicherheit und die korrekte Entsorgungsreihenfolge zu gewährleisten:
// C++/CLI
void SafeUpdateImage(Bitmap^ newBmp) {
if (pictureBox1->InvokeRequired) {
pictureBox1->Invoke(gcnew Action<Bitmap^>(
this, &YourClass::SafeUpdateImage), newBmp);
zurückkehren;
}
Bitmap^ old = safe_cast<Bitmap^>(pictureBox1->Image);
pictureBox1->Image = newBmp; // Zuerst zum neuen Bild wechseln
if (old != nullptr) old->Dispose(); // Altes Bild wieder entsorgen
}
// Fehler: Bitmap ungültig, nachdem der Stream geschlossen wurde
using (var stream = new FileStream(path, FileMode.Open)) {
pictureBox1.Image = new Bitmap(stream);
}
// Richtig: Deep Copy, verlässt sich nicht auf Stream
Bitmap geladen;
using (var stream = new FileStream(path, FileMode.Open))
using (var tmp = new Bitmap(stream)) {
geladen = neue Bitmap(tmp);
}
pictureBox1.Image = geladen;
| Artikel prüfen | Bestätigungsmethode |
|---|---|
| Ob die Bitmap an anderer Stelle entsorgt wird | Ändern Sie Bilder einheitlich über SafeUpdateImage und zeichnen Sie StackTrace auf |
| Ob der Quell-Stream geschlossen ist | Verwenden Sie stattdessen Deep Copynew Bitmap(tmp) |
| Ob threadübergreifend zugegriffen werden soll | Überprüfen Sie „InvokeRequired“, wenn Sie Bilder ändern, und rufen Sie immer wieder den UI-Ausführungsthread auf |
| Erscheint das rote Kreuz sofort oder erst nach einer Weile? | Wenn es sofort auftritt, liegt möglicherweise ein Quellproblem vor. Nach einer Weile kann es sich um ein Timing-Problem handeln. |
Wenn die folgende Fehlermeldung im Stack-Trace angezeigt wird, müssen Entwickler möglicherweise eine Überprüfung durchführenMessageDer Inhalt des Objekts:
at ....MainForm.Dispose(Boolean A_0)
at System.Windows.Forms.Form.WmClose(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
...
Zu diesem Zeitpunkt ist ein Überschreiben oder Erkennen erforderlichWndProcin der MethodeMessage mum die Details zu überprüfen.
Es gibt verschiedene Möglichkeiten zur Überprüfung oder AufzeichnungMessageDer Inhalt des Objekts.
Wenn Sie Zugriff auf den Quellcode des Formulars oder Steuerelements haben, wird das Überschreiben empfohlenWndProcMethode, direkt aufzeichnen oder prüfenMessage mInhalt.
protected override void WndProc(ref Message m)
{
Versuchen Sie es
{
//Den Inhalt der Nachricht aufzeichnen
Console.WriteLine($"Nachrichtendetails: hWnd={m.HWnd}, Msg={m.Msg}, WParam={m.WParam}, LParam={m.LParam}");
base.WndProc(ref m);
}
Catch (Ausnahme ex)
{
Console.WriteLine($"Exception: {ex}");
werfen; // Das ursprüngliche Ausnahmeverhalten beibehalten
}
}
Dieser Code protokolliert bei jedem Empfang einer Nachricht relevante Informationen und ermöglicht dem Entwickler eine weitere Analyse.
Wenn der Fehler auftritt inDisposeIn der Methode können Sie eine Ausnahmebehandlung innerhalb der Methode hinzufügen, um relevante Informationen zu überprüfen.
protected override void Dispose(bool disposing)
{
Versuchen Sie es
{
wenn (entsorgen)
{
// Ressourcen freigeben
}
base.Dispose(entsorgen);
}
Catch (Ausnahme ex)
{
Console.WriteLine($"Ausnahme in Dispose: {ex}");
werfen;
}
}
Dadurch wird sichergestellt, dass wichtige Fehlerinformationen beim Freigeben von Ressourcen nicht ignoriert werden.
Wenn Sie nicht feststellen können, wo der Fehler aufgetreten ist, können Sie die globale Ausnahmebehandlung verwenden, um Stack-Traces und zugehörige Informationen aufzuzeichnen.
AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
{
Exception ex = (Exception)args.ExceptionObject;
Console.WriteLine($"Unhandled Exception: {ex}");
Environment.Exit(1);
};
Sie können Visual Studio verwenden, um Haltepunkte festzulegenWndProcoderDisposeMethode und ÜberprüfungMessageDer Inhalt des Objekts.
HWnd: Das Handle des Fensters, das die Nachricht empfängt.Msg: ID der Nachricht.WParam: Zusätzliche Nachrichteninformationen (Wortparameter).LParam: Zusätzliche Nachrichteninformationen (langer Parameter).WndProcOder seien Sie vorsichtig, wenn Sie den Code ändern, um eine Beeinträchtigung des vorhandenen Verhaltens zu vermeiden.MessageDie Koordinaten oder die ID sind nicht festgelegt. Sie können eine bedingte Beurteilung hinzufügen, um die erforderlichen Informationen zu filtern.In .NET C++/CLI,WmClose(Message& m)JaSystem.Windows.Forms.FormDie interne Schutzmethode kann nicht direkt überschrieben werden. Eine Überschreibung ist jedoch möglichWndProcMethode zum Abfangen und VerarbeitenWM_CLOSENachricht, um eine ähnliche Überschreibung zu erreichenWmCloseWirkung.
#include <Windows.h>
#include <System.Windows.Forms.h>
Verwenden des Namespace-Systems;
Verwenden des Namensraums System::Windows::Forms;
öffentliche Referenzklasse CustomForm: öffentliches Formular
{
geschützt:
// Überschreibung des WmClose-Verhaltens simulieren
void WmClose(Message% m)
{
//Fügen Sie hier ein benutzerdefiniertes WM_CLOSE-Verhalten hinzu
if (MessageBox::Show("Sind Sie sicher, dass Sie das Fenster schließen möchten?", "Bestätigen", MessageBoxButtons::YesNo) == DialogResult::Yes)
{
// Rufen Sie weiterhin das Basisverhalten für ein ordnungsgemäßes Herunterfahren auf
this->Form::WndProc(m);
}
sonst
{
// Verhindern, dass das Fenster geschlossen wird
Rückkehr;
}
}
// WndProc überschreiben, WM_CLOSE-Nachricht abfangen und WmClose aufrufen
virtuelle Leere WndProc(Message% m) überschreiben
{
const int WM_CLOSE = 0x0010;
if (m.Msg == WM_CLOSE)
{
WmClose(m); // Rufen Sie die benutzerdefinierte WmClose-Methode auf
}
sonst
{
// Andere Nachrichten verarbeiten
Form::WndProc(m);
}
}
};
[STAThread]
int main(array<String^>^ args)
{
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
CustomForm^ form = gcnew CustomForm();
form->Text = „Beispiel zum Überschreiben des WmClose-Verhaltens“;
Application::Run(form);
0 zurückgeben;
}
Überschreiben simulierenWmCloseMethode zum Anpassen des Fensterschließverhaltens hier. Verwenden Sie ein Bestätigungsdialogfeld, um den Benutzer zu fragen, ob er das Fenster schließen möchte.
überschreibenWndProcMethode zum AbfangenWM_CLOSENachricht senden und an einen Benutzer delegierenWmCloseVerfahren.
ohne AbfangenWM_CLOSEIm Falle des Aufrufs der BasisklasseWndProcMethode zur Verarbeitung anderer Nachrichten.
WmCloseDie Methode simuliert lediglich ein Überschreiben, verarbeitet die Nachricht jedoch tatsächlich, indem sie sie abfängt.Form::WndProcum sicherzustellen, dass das Basisverhalten ordnungsgemäß funktioniert.InvokeRequiredJaSystem::Windows::Forms::ControlDas Attribut wird verwendet, um zu bestimmen, ob der aktuelle Ausführungsthread der UI-Ausführungsthread ist, zu dem das Steuerelement gehört. Wenn Sie über einen Hintergrundthread auf UI-Steuerelemente zugreifen, sollten Sie verwendenInvokeMarschallen Sie den Vorgang zurück zum UI-Thread.Verwenden des Namespace-Systems;
Verwenden des Namensraums System::Windows::Forms;
Verwenden des Namensraums System::Drawing;
Referenzklasse ImageHelper {
öffentlich:
static void SetImageSafe(PictureBox^ pPictureBox, Bitmap^ b) {
if (pPictureBox == nullptr)
Rückkehr;
if (pPictureBox->InvokeRequired) {
// Verwenden Sie MethodInvoker, um dieselbe Funktion aufzurufen, aber im UI-Thread auszuführen
pPictureBox->Invoke(
gcnew MethodInvoker(gcnew Action
InvokeRequiredKann nur zur Vererbung verwendet werdenControlObjekt (z. B. PictureBox).trueDies bedeutet, dass der aktuelle Ausführungsthread nicht der UI-Ausführungsthread ist und verwendet werden mussInvoke。TupleKapseln Sie mehrere Parameter und übergeben Sie sie zur Verarbeitung an die statische Proxy-Funktion.try-catchDurch das Abfangen von Ausnahmen werden Programmabstürze vermieden.
using namespace System;
using namespace System::Windows::Forms;
using namespace System::Drawing;
void SetImageSafe(PictureBox^ pPictureBox, Bitmap^ b) {
if (pPictureBox->InvokeRequired) {
pPictureBox->Invoke(gcnew MethodInvoker(
gcnew EventHandler(nullptr, &SetImageInvoker)
), gcnew array
Verwenden des Namespace-Systems;
Verwenden des Namensraums System::Windows::Forms;
Verwenden des Namensraums System::Drawing;
void SafeAssignImage(PictureBox^ pPictureBox, Bitmap^ b) {
if (pPictureBox->InvokeRequired) {
pPictureBox->Invoke(gcnew MethodInvoker(gcnew Delegat {
versuche es mit {
if (b != nullptr) {
if (pPictureBox->Image != nullptr)
pPictureBox->Image löschen;
pPictureBox->Image = b;
}
} Catch (System::Exception^ ex) {
Console::WriteLine("Bild konnte nicht festgelegt werden: " + ex->Message);
}
}));
} sonst {
versuche es mit {
if (b != nullptr) {
if (pPictureBox->Image != nullptr)
pPictureBox->Image löschen;
pPictureBox->Image = b;
}
} Catch (System::Exception^ ex) {
Console::WriteLine("Bild konnte nicht festgelegt werden: " + ex->Message);
}
}
}
gcnew MethodInvoker(...)Muss eine legale Delegation sein, die C++11-Lambda-Syntax (z. B. [=]()) wird nicht unterstützt.BeginInvokeübereinstimmenObject^Array und analysieren.Blazor ist ein von Microsoft eingeführtes Front-End-Framework, das es Entwicklern ermöglicht, mithilfe der C#- und Razor-Syntax interaktive Webanwendungen zu erstellen. C#-Code kann im Browser ohne Verwendung von JavaScript ausgeführt werden.
Blazor integriert ASP.NET Core nahtlos, kann mit vorhandenen .NET-Anwendungen kombiniert werden und unterstützt Abhängigkeitsinjektion, Routing, Komponentenarchitektur und andere Funktionen.
Für Entwickler, deren Haupttechnologie .NET ist, bietet Blazor eine Lösung, die der einheitlichen JavaScript-Sprache von Node.js entspricht und es sowohl dem Front-End als auch dem Back-End ermöglicht, C# zu verwenden, wodurch ein konsistenteres Entwicklungserlebnis entsteht.
.NET MAUI (Multi-Plattform App UI) ist ein von Microsoft eingeführtes plattformübergreifendes Anwendungsframework. Sie können C# und XAML verwenden, um einmalig Code zu schreiben und ihn unter Windows, Android, iOS und sogar macOS bereitzustellen.
Der Blazor-Hybridmodus von .NET MAUI kann zum Einbetten der Web-Benutzeroberfläche in native Anwendungen verwendet werden, sodass Entwickler Razor-Komponenten verwenden können, um plattformübergreifende Benutzeroberflächen zu entwickeln und gleichzeitig auf native APIs zuzugreifen.
.NET MAUI ist derzeit die umfassendste plattformübergreifende .NET-Lösung. Es kann mit Blazor kombiniert werden, um eine einheitliche Anwendungsentwicklung für Web-, Desktop- und mobile Plattformen zu erreichen und .NET-Entwicklern ein umfassendes Erlebnis zu bieten, das in Sprache und Technologie konsistent ist.
.NET MAUI kann über API-Aufrufe in die von ASP.NET Core erstellten Back-End-Dienste integriert werden, um eine vollständige „Front-End-App + Back-End-API“-Architektur zu bilden.
ASP.NET Core löst die Probleme von „Back-End-Diensten“ und „Webanwendungen“, während .NET MAUI sich auf „clientseitige plattformübergreifende Apps“ konzentriert. Die beiden schließen sich nicht gegenseitig aus, werden aber oft zusammen verwendet, um eine Komplettlösung zu bilden.
Unten sehen Sie ein Beispiel einer plattformübergreifenden Anwendung, die mit .NET MAUI erstellt wurde und eine Schaltfläche auf dem Bildschirm anzeigt, die beim Klicken die Textanzahl aktualisiert.
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiApp.MainPage">
<VerticalStackLayout Spacing="25" Padding="30">
<Label x:Name="counterLabel"
Text="Sie haben noch nicht auf die Schaltfläche geklickt"
Schriftgröße="24"
HorizontalOptions="Center" />
<Button Text="Klick mich"
Clicked="OnCounterClicked"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ContentPage>
namespaceMauiApp;
öffentliche Teilklasse MainPage: ContentPage
{
int count = 0;
öffentliche Hauptseite()
{
InitializeComponent();
}
private void OnCounterClicked(object sender, EventArgs e)
{
count++;
counterLabel.Text = $"Sie haben {count} Mal geklickt";
}
}
Dieses einfache Beispiel demonstriert die plattformübergreifenden Funktionen von .NET MAUI. Entwickler müssen XAML- und C#-Code nur einmal schreiben, um ihn auf mehreren Plattformen gleichzeitig auszuführen.
MainPage.xaml: UI-Schnittstelle definieren (mit XAML)MainPage.xaml.cs: Backend-Code (C#) verarbeitet EreignisseMauiProgram.cs: Anwendungsstart- und DienstregistrierungspunktPlatformsOrdner: Native Einstellungen für jede Plattform (Android, iOS, Windows, MacCatalyst)Es sind nur wenige Schritte erforderlich, um mit Visual Studio ein .NET MAUI-Projekt zu erstellen, und ein Codesatz kann gleichzeitig auf mehreren Plattformen bereitgestellt werden, was es zur idealen Wahl für moderne C#-Full-End-Entwickler macht.
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyMauiApp.MainPage">
<VerticalStackLayout Spacing="25" Padding="30"
VerticalOptions="Center">
<Label
Text="Willkommen bei .NET MAUI!"
SemanticProperties.HeadingLevel="Level1"
Schriftgröße="32"
HorizontalOptions="Center" />
<Schaltfläche
x:Name="counterBtn"
Text="Klick mich!"
Clicked="OnCounterClicked"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ContentPage>
Verwenden des Systems;
Namespace MyMauiApp;
öffentliche Teilklasse MainPage: ContentPage
{
int count = 0;
öffentliche Hauptseite()
{
InitializeComponent();
}
private void OnCounterClicked(object sender, EventArgs e)
{
count++;
counterBtn.Text = $"Sie haben {count} Mal geklickt";
}
}
Spacing(Abstand) undPadding(innerer Abstand).x:Namefür den Zugriff durch das C#-Backend,ClickedDas Attribut gibt die Methode zur Verarbeitung von Klickereignissen an.InitializeComponent(): Initialisieren Sie die in XAML definierte UI-Struktur.OnCounterClicked: Klicken Sie auf die Ereignisverarbeitungsmethode und aktualisieren Sie den Schaltflächentext jedes Mal, wenn auf die Schaltfläche geklickt wird.counterBtn.Text: Steuern Sie die Eigenschaften der Schaltfläche über den in XAML angegebenen Namen.Nach der Ausführung werden in der Mitte des Bildschirms eine Willkommensnachricht und eine Schaltfläche angezeigt. Jedes Mal, wenn Sie auf die Schaltfläche klicken, wird der Text auf der Schaltfläche zu „Sie haben X-mal geklickt“ aktualisiert.
using Microsoft.Extensions.Logging;
namespace MyMauiApp;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp() // Geben Sie den Einstiegspunkt der Anwendung an
.ConfigureFonts(fonts =>
{
Schriftarten.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
Schriftarten.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
#if DEBUG
builder.Logging.AddDebug();
#endif
return builder.Build();
}
}
MauiAppBeispiel.App.xaml.csApp-Kategorie) ist dies die Stammkomponente der Anwendung.builder.Services.AddSingleton<MainPage>();Dadurch kann MainPage mithilfe der Abhängigkeitsinjektion geladen werden, beispielsweise in der App-Klasse
serviceProvider.GetService<MainPage>()。
builder.Services.AddTransient<MyViewModel>();
builder.Services.AddSingleton<IMyService, MyService>();
MauiProgram.csGenau wie bei ASP.NET CoreStartup.csoderProgram.cs, ist das Haupteinstellungszentrum für den Anwendungsstart und die Dienstregistrierung. Bei der Entwicklung großer MAUI-Anwendungen kommt es häufig vor, diese Datei zu erweitern, um DI, Protokollierung, Komponenteneinstellungen usw. hinzuzufügen.
Die Bereitstellung von .NET C++ (C++/CLI oder .NET-basierte C++-Anwendungen) auf Ubuntu-Systemen muss je nach Projekttyp separat behandelt werden. Wenn Sie reines C++/CLI verwenden (basierend auf .NET Framework), können Sie es nicht direkt unter Linux ausführen und müssen es verwendenPlattformübergreifende Technologie für .NET 6/7/8(Zum Beispiel C++/CLR → C# oder die Verwendung von C++/Native mit .NET-Interoperabilität).
Abhängig von der Art der Anwendung kann die Bereitstellung in drei Szenarien unterteilt werden:
Installieren Sie das .NET SDK und die erforderlichen Tools in Ubuntu:
sudo apt update
sudo apt install -y dotnet-sdk-8.0
sudo apt install -y build-essential
(Kann je nach Ausführung ausgetauscht werdendotnet-sdk-8.0fürdotnet-sdk-7.0oder andere Versionen)
Wenn Sie .NET C# als Hauptprogramm verwenden und die C++-Bibliothek aufrufen:
# Erstellen Sie die Hauptanwendung
dotnet neue Konsole -n MyApp
cdMyApp
# Erstellen Sie eine native Funktionsbibliothek
mkdir nativ
CD nativ
nano add.cpp
add.cpp-Beispiel:
extern "C" int add(int a, int b) {
return a + b;
}
Als gemeinsam genutzte Funktionsbibliothek kompilieren:
g++ -fPIC -shared -o libadd.so add.cpp
BeiProgram.csdazu kommen:
using System;
using System.Runtime.InteropServices;
class Program
{
[DllImport("libadd.so", EntryPoint="add")]
public static extern int Add(int a, int b);
static void Main()
{
Console.WriteLine("3 + 5 = " + Add(3, 5));
}
}
Kehren Sie zum Projektstammverzeichnis zurück und führen Sie Folgendes aus:
dotnet run
Wenn das Ausgabeergebnis ist3 + 5 = 8, was auf eine erfolgreiche Bereitstellung hinweist.
Programme können in unabhängig ausführbare Bereitstellungsdateien gepackt werden:
dotnet publish -c Release -r linux-x64 --self-contained true
Die generierte ausführbare Datei befindet sich inbin/Release/net8.0/linux-x64/publish/。
Anwendungen können mit Docker in Containern verpackt werden, um auf jedem Ubuntu-System ausgeführt zu werden:
# Dockerfile-Beispiel
VON mcr.microsoft.com/dotnet/runtime:8.0
WORKDIR/app
KOPIEREN ./veröffentlichen .
KOPIEREN ./native/libadd.so /usr/lib/
ENTRYPOINT ["./MyApp"]
Container erstellen und ausführen:
docker build -t myapp .
docker run --rm myapp
.soFunktionsbibliothek, die von Windows verwendet wird.dll。LD_LIBRARY_PATH。Eine praktikable Möglichkeit, .NET C++-Programme auf Ubuntu bereitzustellen, ist normalerweiseNative C# + C++-Bibliothek”-Struktur. Wenn das ursprüngliche Programm auf C++/CLI basiert, muss es neu gestaltet werden, um plattformübergreifendes .NET zu unterstützen. Die am meisten empfohlene Methode ist die Verwendung von .NET 8 + nativen C++-Modulen mit Docker, um einen stabilen, portablen und konsistenten Bereitstellungsprozess zu erreichen.
email: [email protected]