Entwickelt von Microsoft



Visual Studio

Einführung in die Entwicklungsumgebung

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.

Hauptfunktionen

Versionsunterschied

Plattformunterstützung

Visual Studio unterstützt Windows-Betriebssysteme und Visual Studio für Mac ist für macOS konzipiert.

Häufige Verwendungen



Formatierung der Visual Studio-Syntax

1. Code automatisch formatieren

Visual Studio bietet Tastenkombinationen zum schnellen Formatieren von Code, die für verschiedene Programmiersprachen geeignet sind: - Verwenden Sie die Tastenkombinationen „Strg + K, Strg + D“, um die gesamte Datei zu formatieren. - Verwenden Sie die Tastenkombinationen „Strg + K, Strg + F“, um den ausgewählten Codeblock zu formatieren.

2. Aktivieren Sie die automatische Formatierung

Visual Studio unterstützt die automatische Codeformatierung beim Speichern von Dateien: 1. Klicken Sie auf **Extras > Optionen**. 2. Gehen Sie zu **Texteditor > [Sprache] > Codierungsstil > Allgemein**. 3. Aktivieren Sie **Code beim Speichern der Datei neu formatieren**.

3. Ändern Sie die Formatierungseinstellungen

1. Öffnen Sie **Extras > Optionen**. 2. Gehen Sie zu **Texteditor > [Sprache] > Codierungsstil > Formatierung**. 3. Hier können Sie detaillierte Formatierungseinstellungen wie Einrückung, Klammerstil und Leerzeilen anpassen.

4. Verwenden Sie Codierungsstilprofile

Visual Studio unterstützt Codierungsstil-Konfigurationsdateien (.editorconfig), um den Codierungsstil des Teams zu vereinheitlichen: 1. Erstellen Sie eine neue „.editorconfig“-Datei im Stammverzeichnis des Projekts. 2. Formatregeln definieren, zum Beispiel:
   [*.cs]
   indent_style = space
   indent_size = 4
   
3. Nachdem die Datei gespeichert wurde, folgt die Formatierung automatisch diesen Regeln.

5. Installieren Sie Erweiterungstools

Wenn die integrierte Formatierungsfunktion Ihren Anforderungen nicht entspricht, können Sie Erweiterungstools installieren: - Suchen und installieren Sie Tools im **Extension Manager**, wie zum Beispiel **CodeMaid** oder **ReSharper**. – Diese Tools bieten mehr Codeformatierungs- und Refactoring-Optionen.

6. Formatprobleme schnell beheben

Wenn Formatierungsprobleme auftreten, können Sie schnelle Lösungen verwenden: - Klicken Sie mit der rechten Maustaste auf den problematischen Code und wählen Sie **Schnellaktionen und Refactoring**. - Wählen Sie **Datei formatieren** oder **Auswahl formatieren**.

7. Zusammenfassung

- Verwenden Sie Tastenkombinationen, um Code schnell zu formatieren. - Aktivieren Sie die automatische Formatierung beim Speichern von Dateien. - Verwenden Sie „.editorconfig“, um den Codestil zu vereinheitlichen. - Installieren Sie Erweiterungstools, um die Formatierungsfunktionen zu verbessern. - Nutzen Sie die Schnellkorrekturfunktion sinnvoll, um die Lesbarkeit des Codes zu verbessern.

.NET SDK-Version

Überprüfen Sie das installierte .NET SDK

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.

Laden Sie das .NET SDK herunter und installieren Sie es

  1. Gehe zuOffizielle .NET-Downloadseite
  2. Wählen Sie die entsprechende Version (z. B. .NET 9) und die Betriebssystemplattform (Windows, macOS, Linux) aus.
  3. Laden Sie das Installationsprogramm herunter und führen Sie es aus

Aktivieren Sie Eingabeaufforderungszeichen in Visual Studio

  1. Öffnen Sie Visual Studio
  2. Klicken Sie auf die Menüspalte:Werkzeuge > Befehlszeile > Zeichen für die Entwickler-Eingabeaufforderung
  3. Es öffnet sich ein Eingabeaufforderungsfenster mit dem eingestellten Entwicklungsumgebungspfad, der direkt verwendet werden kann.dotnetBefehl

Wenn diese Option nicht angezeigt wird, können Sie auch im Windows-Startmenü nach „Entwickler-Eingabeaufforderung für VS“ suchen, um sie zu öffnen.

Verwenden Sie das integrierte Terminal von Visual Studio (unterstützt ab 2022).

  1. Klicken Sie in Visual Studio auf:Anzeigen > TerminalOder verwenden Sie TastenkombinationenCtrl + `
  2. Terminaltyp auswählen (z. B. PowerShell oder CMD)
  3. Kann direkt eingegeben werdendotnetBefehl zum Überprüfen oder Betreiben des SDK


C#-Sprache

Sprachmerkmale

grundlegendes Beispiel

Verwenden des Systems;

Klassenprogramm {
    static void Main() {
        string name = "world";
        Console.WriteLine($"Hallo, {name}!");
    }
}

Häufige Anwendungsbereiche

Erweiterte Funktionen



CS8618 Non-Nullable ist nicht initialisiert

Warninhalt

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

Beispielfragen

öffentliche Klasse Produkt {
    öffentlicher String Name { get; Satz; } // Warnung CS8618
}

Lösung

Methode 1: Wert im Konstruktor zuweisen (empfohlen)

public class Product {
    public string Name { get; set; }

    public Product(string name) {
        Name = name;
    }
}

Methode 2: Geben Sie den Attributen Standardwerte

public class Product {
    public string Name { get; set; } = string.Empty;
}

Methode 3: Null zulassen

public class Product {
    public string? Name { get; set; }
}

→ Gilt fürNamevernünftigerweise erlaubt seinnullSituation.

Methode 4: VerwendenrequiredModifikatoren (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

Anregung



.NET-Programme

Konvertieren Sie Float in String, um wissenschaftliche Notation zu vermeiden

Problemstellung

In C++/CLI, wenn Sie verwendenfloatUndSystem::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).

Lösung

VerfügbarSystem::String::FormatoderToStringVerwenden Sie eine Formatzeichenfolge, um die Anzahl der Dezimalstellen anzugeben und die wissenschaftliche Notation zu vermeiden.

Beispielprogramm

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);

Codeanweisungen formatieren



.NET und std str transkodieren verstümmelte Zeichen

Grund

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.

Lösung 1: Verwendenmarshal_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.

---

Lösung 2: Wenn Sie sicher sind, dass std::string UTF-8 ist, können Sie es manuell 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());
---

Lösung 3: Wenn die Quelle iststd::wstring

Wenn 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());
---

Anregung



.NET List

Grundlegende Einführung

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.

Deklaration und Initialisierung

//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" });
}

Elemente hinzufügen, entfernen und darauf zugreifen

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();

Traverse-Liste

// 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);
}

Gemeinsame Eigenschaften und Methoden

Eigenschaften/Methodenveranschaulichen
CountAktuelle Anzahl der Elemente
CapacityInterne 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

Beispielausgabe

10
15
30


.NET Dictionary

Grundlegende Einführung

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.

Deklaration und Initialisierung

//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;
}

Elemente hinzufügen, ändern, löschen und darauf zugreifen

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);
}

Traverse-Wörterbuch

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);
}

Gemeinsame Eigenschaften und Methoden

Eigenschaften/Methodenveranschaulichen
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
CountDie aktuelle Anzahl der Elemente im Wörterbuch
KeysGibt eine Sammlung aller Schlüssel zurück
ValuesGibt eine Sammlung aller Werte zurück

Beispielausgabe

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


Rufen Sie mit .NET C++ die neueste Datei in einem Verzeichnis ab

Einführung

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.

Beispielcode

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;
}

Code-Erklärung

Anwendungsszenarien

Dinge zu beachten



.NET: System.Reflection

1. Was ist System.Reflection?

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.

2. Zweck von System.Reflection

3. Allgemeine System.Reflection-Kategorien

4. Anwendungsbeispiele

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

5. Häufige Anwendungsszenarien



System::Management::ManagementClass

verwenden

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.

Namensraum

System::ManagementbefindetSystem.Management.dllIm Namespace müssen Sie eine Referenz hinzufügen, bevor Sie diese Funktion verwenden können.

Häufige Verwendungen

Einfaches Beispiel

// 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;
}

Häufige Fehler und Fehlerbehebung

Verwandte Kategorien



Beheben Sie den Fehler „Ungültige Klasse“ in Win32_NetworkAdapterConfiguration

Beschreibung des Fehlercodes

Der Fehlercode „0x80041010“ tritt bei der Verwendung von „System::Management::ManagementClass("Win32_NetworkAdapterConfiguration")“ auf und weist darauf hin, dass WMI die Klasse nicht finden kann, was normalerweise Folgendes bedeutet:

Lösungsschritte

Hier ist eine vollständige Lösung zum Neuerstellen des WMI-Repositorys für Windows 10/11:
net stop winmgmt
winmgmt /resetrepository
net start winmgmt

veranschaulichen

Nach der Reparatur überprüfen

Sie können eine der folgenden Methoden verwenden, um zu bestätigen, ob die Reparatur erfolgreich war:Methode 1: PowerShell-Abfrage
Get-WmiObject Win32_NetworkAdapterConfiguration
Methode 2: WMI-Testtool (wbemtest)
  1. Drücken Sie Win + R und geben Sie einwbemtest
  2. Klicken Sie auf „Verbinden“ und geben Sie einroot\cimv2und verbinden
  3. Klicken Sie auf „Abfrage“ und geben Sie einSELECT * FROM Win32_NetworkAdapterConfiguration

Zusätzliche Vorschläge



.NET C++ liest eine HTML-Tabelle

Überblick

In .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.

Schritte zur Verwendung des Html Agility Pack

  1. Erstellen Sie ein C++/CLI-Projekt:Stellen Sie sicher, dass es sich bei Ihrem Projekt um ein C++/CLI-Projekt handelt (z. B. CLR-Konsolenanwendung oder Windows Forms-App).
  2. Holen Sie sich das HTML Agility Pack:
  3. Programmcode-Implementierung (C++/CLI):

Codebeispiel

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;
}

Erläuterung der wichtigsten technischen Punkte



.NET C++ HTTP API-Server

Verwendung der HttpListener-Klasse

HttpListenerMit der Klasse können Sie einen einfachen, selbstgehosteten HTTP-Server in einer C++/CLI-Anwendung erstellen.

Projektanforderungen und -einstellungen

Erstellen Sie ein C++/CLI-Projekt in Visual Studio (z. B. CLR-Konsolenanwendung) und stellen Sie sicher, dass darauf verwiesen wirdSystem.dll

Codebeispiel

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;
}

Hinweis zu Ausführungsberechtigungen

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.



ASP.NET Core C# HTTP-API-Server

Überblick

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.

Projekteinrichtung (mit CLI)

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

Programmcode-Beispiel: Program.cs

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();

Ausführung und Prüfung

  1. Führen Sie den Server aus:Im Projektverzeichnis ausführendotnet run
  2. Testendpunkt:Server werden normalerweise standardmäßig verwendethttp://localhost:5000oderhttps://localhost:7000laufen.

Wichtige technische Punkte



So führen Sie .NET-Programme auf Chromebook aus

1. Führen Sie .NET mit der Linux-Unterstützung des Chromebooks aus

Die meisten modernen Chromebooks unterstützen die Ausführung von Linux-Anwendungen über das Crostini-Projekt.

  1. Aktivieren Sie Linux-Funktionen:
  2. Installieren Sie das .NET SDK:
  3. Kompilieren Sie die .NET-Anwendung und führen Sie sie im Terminal aus.

2. Verwenden Sie eine Cloud-Entwicklungsumgebung

Mit der Cloud-Plattform können Sie .NET-Programme auf Ihrem Chromebook ausführen, ohne lokal Software installieren zu müssen.

3. Verwenden Sie Docker-Container, um .NET auszuführen

Chromebooks unterstützen die Verwendung von Containern zum Ausführen von Anwendungen, und Sie können eine .NET-Umgebung über Docker starten.

  1. Installieren Sie Docker in einer Linux-Umgebung.
  2. Laden Sie das .NET-Container-Image herunter:
    docker pull mcr.microsoft.com/dotnet/runtime:7.0
  3. Führen Sie Ihr .NET-Programm in einem Container aus.

4. Nutzen Sie plattformübergreifende Unterstützung

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.

  1. Kompilieren Sie Ihre Anwendung auf einem beliebigen Computer:
    dotnet publish -c Release -r linux-x64 --self-contained
  2. Übertragen Sie das kompilierte Archiv auf Ihr Chromebook und führen Sie es mit der mitgelieferten Laufzeit aus.

Dinge zu beachten



.NET-UI-Programmierung

Haupttechnologie

Einfaches WinForms-Beispiel

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());
    }
}

Einfaches WPF-Beispiel (XAML + C#)

// 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!");
}

Einfaches MAUI-Beispiel

// 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");
}

Beratung zur Auswahl der UI-Technologie

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)


.NET durchläuft untergeordnete Steuerelemente, um Eigenschaften stapelweise zu ändern

Die folgenden Beispiele zeigen die VerwendungControls->GetEnumerator()Methode zum schrittweisen Durchlaufen aller untergeordneten Steuerelemente in einem .NET-Formular und zum stapelweisen Ändern ihrer Eigenschaften.

Beispielcode:

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());
    }
}

veranschaulichen

**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.

Ausführungseffekt

Nach der Ausführung ändert sich die Hintergrundfarbe aller Steuerelemente in Hellblau und alle „TextBox“-Steuerelemente werden auf nicht bearbeitbar gesetzt.



.NET MessageBox

1. Grundlegende Syntax und allgemeine Überladung

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)
);

2. Verarbeiten Sie den Rückgabewert (DialogResult)

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
}

3. Gemeinsame Aufzählungswertreferenz

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.

4. Fortgeschrittene Technik: Kombinieren Sie std::string oder float

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“

Häufige Fehler und Lösungen



Benutzerdefiniertes .NET-Eingabedialogfeld

1. Warum unterstützt MessageBox Edit Box nicht?

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.


2. Implementieren Sie eine benutzerdefinierte InputBox

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;
    }
};

3. So rufen Sie das Hauptprogramm auf

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);
        }
    }
}

4. Planvergleich

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.

Häufige Fallstricke



PictureBox-Rotkreuzfehler

Phänomenbeschreibung

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.

Häufige Ursachen

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

Vergleich der Fehlermeldungen

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

Debug-Schritte

Da das Problem des Roten Kreuzes möglicherweise nicht oft auftritt, wird empfohlen, in der folgenden Reihenfolge vorzugehen:

  1. Abfangen von OnPaint-Ausnahmen: PictureBox erben, überschreibenOnPaint, Verwenden Sie try/catch, um Ausnahmen abzufangen und auszugebenDebug.WriteLine, nach der Erfassung ausführenthis.Image = nullVermeiden Sie anhaltende rote Kreuze.
  2. Ausnahmemeldung bestätigen: Beachten Sie die Meldung im Visual Studio-Ausgabefenster. Sehen Sie sich die Tabelle der Fehlermeldungen oben an, um die Grundursache zu finden.
  3. Verfolgen des Bitmap-Lebenszyklus: Jedes Mal, wenn Bitmap entsorgen zugewiesen wird, aufzeichnenEnvironment.StackTraceFinden Sie heraus, wer die Ressource zu welchem ​​Zeitpunkt freigegeben hat.
  4. Verhindern Sie die Entsorgung einer Bitmap, die noch verwendet wird: Vor der Entsorgung prüfen Ob die PictureBox noch die Bitmap enthält, wenn ja, zuerstImageAuf null setzen und dann entsorgen.

Sicherer Bildwechselmodus

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
}

Der richtige Weg, eine Bitmap aus Stream zu erstellen

// 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;

Checkliste zur Fehlerbehebung

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.


Rufen Sie den Inhalt der Nachricht m in .NET StackTrace ab

Problemhintergrund

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.

Lösung

Es gibt verschiedene Möglichkeiten zur Überprüfung oder AufzeichnungMessageDer Inhalt des Objekts.

1. Überschreiben Sie die WndProc-Methode

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.

2. Fügen Sie der Dispose-Methode eine Ausnahmebehandlung hinzu

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.

3. Verwenden Sie die globale Ausnahmebehandlung

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);
};

    

4. Verwenden Sie das Debug-Tool, um die Nachricht zu überprüfen

Sie können Visual Studio verwenden, um Haltepunkte festzulegenWndProcoderDisposeMethode und ÜberprüfungMessageDer Inhalt des Objekts.

Schlüsseleigenschaften des Message-Objekts

Dinge zu beachten



Überschreiben von Form.WmClose(Message& m) in .NET C++

Problemhintergrund

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.

Vollständiges Beispiel

#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;
}
    

Code-Erklärung

1. WmClose-Methode

Ü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.

2. Überschreiben Sie WndProc

überschreibenWndProcMethode zum AbfangenWM_CLOSENachricht senden und an einen Benutzer delegierenWmCloseVerfahren.

3. Fahren Sie mit der Verarbeitung anderer Nachrichten fort

ohne AbfangenWM_CLOSEIm Falle des Aufrufs der BasisklasseWndProcMethode zur Verarbeitung anderer Nachrichten.

Dinge zu beachten



System::Windows::Forms::Control::InvokeRequired

Gebrauchsanweisung

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.

Vollständiges Beispiel: PictureBox-Bilder festlegen

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(ImageHelper::InvokeCallback), 
                gcnew Tuple(pPictureBox, b))
            );
        } sonst {
            ApplyImage(pPictureBox, b);
        }
    }

privat:
    // Marshalled-Funktions-Proxy
    static void InvokeCallback(Object^ state) {
        Tupel^ args = static_cast^>(Staat);
        ApplyImage(args->Item1, args->Item2);
    }

    // Tatsächliche Ausführung der Einstellungsbildlogik
    static void ApplyImage(PictureBox^ pPictureBox, Bitmap^ b) {
        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);
        }
    }
};

Konzentrieren Sie sich auf die Organisation



Schützen Sie pPictureBox->Image = b, um Programmabstürze zu vermeiden

Mögliche Absturzursachen

Empfohlene Vorgehensweisen zum sicheren Einrichten von Bildern


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{ pPictureBox, b });
    } sonst {
        SetImageInternal(pPictureBox, b);
    }
}

void SetImageInvoker(Object^ sender, EventArgs^ e) {
    // Wenn diese Version nicht verwendet wird, kann sie ignoriert und als vollständige Referenz beibehalten werden.
}

void SetImageInternal(PictureBox^ pPictureBox, Bitmap^ b) {
    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);
    }
}

Vereinfachte Version (anonyme Delegation wird empfohlen)

Wenn Sie „pPictureBox->Image = b;“ nur schnell schützen möchten, ist die folgende Schreibmethode direkter und effektiver:
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);
        }
    }
}

Weitere Informationen



Richten Sie mit Blazor eine einheitliche Sprach-Frontend-Entwicklungsumgebung ein

Was ist Blazor?

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.

Vorteile für Entwickler

Blazor-Modus

Integration mit .NET

Blazor integriert ASP.NET Core nahtlos, kann mit vorhandenen .NET-Anwendungen kombiniert werden und unterstützt Abhängigkeitsinjektion, Routing, Komponentenarchitektur und andere Funktionen.

Bereitstellungsmethode

Zusammenfassung

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.



Erstellen Sie plattformübergreifende Anwendungen mit .NET MAUI

Was ist .NET MAUI?

.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.

Vorteile für Entwickler

Unterstützte Plattformen

Blazor-Hybridmodus

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.

Integration und Bereitstellung

Zusammenfassung

.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.



Vergleich zwischen ASP.NET Core und .NET MAUI

Position

Support-Plattform

Entwicklungssprache

Benutzeroberfläche und Interaktion

Anwendbare Szenarien

Integration

.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.

Zusammenfassen

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.



Einfaches .NET MAUI-Anwendungsbeispiel

Erstellen Sie eine einfache .NET MAUI-Anwendung

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.

Hauptdatei: MainPage.xaml

<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>

Backend-Code: MainPage.xaml.cs

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";
    }
}

Ausführungsmethode

Zusammenfassung

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.



Erstellen Sie ein .NET MAUI-Projekt in Visual Studio

Schritt 1: Installieren Sie die erforderlichen Tools

Schritt 2: Erstellen Sie ein neues MAUI-Projekt

  1. Öffnen Sie Visual Studio
  2. Klicken Sie auf „Neues Projekt erstellen“
  3. Suchen und wählen Sie „.NET MAUI App"Vorlage
  4. Klicken Sie auf „Weiter“ und geben Sie den Projektnamen und den Speicherort ein
  5. Klicken Sie auf „Erstellen“, um die Projektinitialisierung abzuschließen

Schritt 3: Verstehen Sie die Projektstruktur

Schritt 4: Führen Sie die Anwendung aus

Weitere Informationen

Zusammenfassung

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.



MainPage.xaml Beispiele und Erklärungen

Beispiel: MainPage.xaml

<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>

Entsprechende MainPage.xaml.cs

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";
    }
}

Komponentenbeschreibung

C#-Codebeschreibung

Ergebniseffekt

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.



Beispiele und Erklärungen für MauiProgram.cs

Beispiel: MauiProgram.cs

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();
    }
}

Funktionsbeschreibung

Gemeinsame Erweiterungen

Zusammenfassen

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.



Bereitstellen von .NET C++-Programmen auf Ubuntu

Vorwort

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).

Bereitstellungsübersicht

Abhängig von der Art der Anwendung kann die Bereitstellung in drei Szenarien unterteilt werden:

  1. Reine C++-Anwendung:Mit g++ kompiliert und direkt auf Ubuntu ausgeführt.
  2. .NET Managed C++ (C++/CLI):Muss für C# oder C++/CLI für Core, unterstützt von .NET 6 und höher (unterstützt nur von Windows), neu gestaltet werden.
  3. Hybridanwendung:C++ stellt hauptsächlich C# und Leistungsmodule in Form dynamischer Funktionsbibliotheken (.so) bereit.

Umgebungsinstallation

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)

Projekt erstellen

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

Aufrufen von C++-Bibliotheken in C# (.NET)

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));
    }
}

Ausführung und Prüfung

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.

Release-Bereitstellungsversion

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/

Docker-Container-Bereitstellung (optional)

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

Dinge zu beachten

Zusammenfassen

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]
T:0000
資訊與搜尋 | 回dev首頁
email: Yan Sa [email protected] Line: 阿央
電話: 02-27566655 ,03-5924828
阿央
泱泱科技
捷昱科技泱泱企業