Разработано Microsoft



Visual Studio

Введение в среду разработки

Visual Studio — это интегрированная среда разработки (IDE), разработанная Microsoft, которая поддерживает несколько языков программирования, таких как C++, C#, VB.NET, Python, JavaScript и т. д. Подходит для разработки настольных приложений, веб-сайтов, облачных сервисов и мобильных приложений.

Основные функции

Разница версий

Поддержка платформы

Visual Studio поддерживает операционные системы Windows, а Visual Studio для Mac разработан для macOS.

Обычное использование



Форматирование синтаксиса Visual Studio

1. Автоматическое форматирование кода

Visual Studio предоставляет сочетания клавиш для быстрого форматирования кода, подходящего для различных языков программирования: - Используйте сочетания клавиш `Ctrl + K, Ctrl + D` для форматирования всего файла. - Используйте сочетания клавиш `Ctrl + K, Ctrl + F`, чтобы отформатировать выбранный блок кода.

2. Включите автоматическое форматирование.

Visual Studio поддерживает автоматическое форматирование кода при сохранении файлов: 1. Нажмите **Инструменты > Параметры**. 2. Откройте **Текстовый редактор > [Язык] > Стиль кодирования > Общие**. 3. Установите флажок **Переформатировать код при сохранении файла**.

3. Измените настройки форматирования.

1. Откройте **Инструменты > Параметры**. 2. Откройте **Текстовый редактор > [Язык] > Стиль кодирования > Форматирование**. 3. Здесь вы можете настроить подробные параметры форматирования, такие как отступ, стиль скобок и пустые строки.

4. Используйте профили стилей кодирования

Visual Studio поддерживает файлы конфигурации стиля кодирования (.editorconfig) для унификации стиля кодирования команды: 1. Создайте новый файл `.editorconfig` в корневом каталоге проекта. 2. Определите правила формата, например:
   [*.cs]
   indent_style = space
   indent_size = 4
   
3. После сохранения файла форматирование будет автоматически соответствовать этим правилам.

5. Установите инструменты расширения

Если встроенная функция форматирования не соответствует вашим потребностям, вы можете установить инструменты расширения: - Найдите и установите инструменты в **Extension Manager**, например **CodeMaid** или **ReSharper**. — Эти инструменты предоставляют больше возможностей форматирования и рефакторинга кода.

6. Быстрое исправление проблем с форматом

При возникновении проблем с форматированием вы можете воспользоваться быстрыми исправлениями: – Щелкните правой кнопкой мыши проблемный код и выберите **Быстрые действия и рефакторинг**. – Выберите **Форматировать файл** или **Выбор формата**.

7. Резюме

- Используйте сочетания клавиш для быстрого форматирования кода. - Включить автоматическое форматирование при сохранении файлов. - Используйте `.editorconfig` для унификации стиля кода. - Установите инструменты расширения для улучшения возможностей форматирования. - Используйте функцию быстрой коррекции для улучшения читаемости кода.

Версия .NET SDK

Проверьте установленный .NET SDK

Вы можете проверить версию .NET SDK, установленную на вашем компьютере, с помощью инструмента командной строки:

dotnet --list-sdks

Если появится результат, подобный следующему, это указывает на установленную версию SDK:

8.0.100 [C:\Program Files\dotnet\sdk]
9.0.100-preview.3.24172.9 [C:\Program Files\dotnet\sdk]

Если версия, которую вы хотите использовать, не отображается, это означает, что она еще не установлена.

Загрузите и установите .NET SDK

  1. Перейти кОфициальная страница загрузки .NET
  2. Выберите соответствующую версию (например, .NET 9) и платформу операционной системы (Windows, macOS, Linux).
  3. Загрузите и запустите установщик

Включите символы командной строки в Visual Studio

  1. Открыть Visual Studio
  2. Щелчок по столбцу меню:Инструменты > Командная строка > Символы командной строки разработчика
  3. Откроется окно командной строки с установленным путем к среде разработки, который можно использовать напрямую.dotnetЗаказ

Если вы не видите эту опцию, вы также можете использовать меню «Пуск» Windows для поиска «Командной строки разработчика для VS», чтобы открыть ее.

Используйте встроенный терминал Visual Studio (поддерживается начиная с 2022 г.)

  1. В Visual Studio нажмите:Посмотреть > ТерминалИли используйте сочетания клавишCtrl + `
  2. Выберите тип терминала (например, PowerShell или CMD)
  3. Можно ввести напрямуюdotnetКоманда для проверки или работы SDK


язык С#

языковые особенности

базовый пример

использование системы;

класс Программа {
    статическая пустота Main() {
        имя строки = «мир»;
        Console.WriteLine($"Привет, {имя}!");
    }
}

Общие области применения

Расширенные функции



CS8618 Non-Nullable не инициализируется

Предупреждающее содержание

CS8618 означает: в конце конструктораНе обнуляемыйСвойства не инициализированы, поэтому компилятор C# предупреждает, что это может бытьnull

Примеры вопросов

продукт общественного класса {
    общедоступная строка Имя {get; набор; } // Предупреждение CS8618
}

Решение

Способ 1. Присвойте значение в конструкторе (рекомендуется).

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

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

Способ 2: Присвоить атрибутам значения по умолчанию

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

Способ 3: разрешить значение NULL

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

→ Применимо кNameразумно разрешено бытьnullситуация.

Метод 4: ИспользуйтеrequiredМодификаторы (поддерживаются C# 11+)

продукт общественного класса {
    общедоступная обязательная строка Имя {get; набор; }
}

// Вызывающий должен быть инициализирован
var p = новый продукт {Name = «Мобильный телефон» }; // ХОРОШО

предположение



.NET-программы

Преобразуйте число с плавающей запятой в строку, чтобы избежать научной записи

Постановка задачи

В C++/CLI, если вы используетеfloatиSystem::StringСложите, например:

System::String^ tmpStr = "Value: " + someFloat;
когдаsomeFloatЕсли он очень большой или очень маленький, он может автоматически отображаться в экспоненциальном формате (например, 1.23E+05).

Решение

ДоступныйSystem::String::FormatилиToStringИспользуйте строку формата, чтобы указать количество десятичных знаков и избегать научной записи.

Пример программы

использование пространства имен System;

значение с плавающей запятой = 123456,789f;

// Метод 1: String::Format
String^ tmpStr1 = String::Format("Значение: {0:F2}", value); // F2 представляет 2 цифры после десятичной точки
Консоль::WriteLine(tmpStr1);

// Метод 2: формат соответствия ToString
String^ tmpStr2 = "Значение: " + value.ToString("F2");
Консоль::WriteLine(tmpStr2);

//Метод 3: Больше цифр
String^ tmpStr3 = "Значение: " + value.ToString("F6");
Консоль::WriteLine(tmpStr3);

Инструкции по форматированию кода



.NET и std str перекодируют искаженные символы

причина

В С++/CLIgcnew System::String(stdStr.c_str())воляstd::string(обычноANSI / UTF-8кодирование) непосредственно в .NET.System::StringSystem::StringОжиданиеUTF-16
еслиstdStrЕсли он содержит символы, отличные от ASCII (например, китайские), символы будут отображаться искаженно.

Решение 1. Используйтеmarshal_as(предположение)

Пространство имен должно быть заключено в кавычки:

#include <msclr/marshal_cppstd.h>
используя пространство имен msclr::interop;

std::string stdStr = "Китайский тест";
System::String^ netStr = marshal_as<System::String^>(stdStr);

✅ Этот метод позволяет автоматически правильно конвертировать UTF-8/ANSI в .NET Unicode.

---

Решение 2. Если вы уверены, что std::string имеет формат UTF-8, вы можете преобразовать его вручную.

#include <codecvt>
#include <locale>

std::string utf8Str = u8"Китайский тест";
std::wstring WideStr = std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>{}.from_bytes(utf8Str);
System::String^ netStr = gcnew System::String(wideStr.c_str());
---

Решение 3. Если источникstd::wstring

Если на стороне C++ изначально используются широкие строки символов, просто используйте их напрямую:

std::wstring wstr = L"Китайский тест";
System::String^ netStr = gcnew System::String(wstr.c_str());
---

предположение



.NET List

Основное введение

В .NET (такие языки, как C++/CLI, C#, VB.NET и т. д.)List<T>это общая категория коллекции, Может хранить любые типы данных (T) и обеспечивать функцию динамического изменения размера. оно принадлежитSystem::Collections::Genericпространство имен.

Объявление и инициализация

//Импортируем пространство имен
использование пространства имен System;
используя пространство имен System::Collections::Generic;

интервал основной()
{
    // Объявляем список для хранения целых чисел
    List<int>^ Numbers = gcnew List<int>();

    //Добавляем элементы непосредственно во время инициализации
    List<String^>^ имена = gcnew List<String^>({ "Алиса", "Боб", "Чарли" });
}

Добавляйте, удаляйте и получайте доступ к элементам

List<int>^ nums = gcnew List<int>();

//Добавляем новый элемент
числа->Добавить(10);
числа->Добавить(20);
числа->Добавить(30);

//Вставляем в указанную позицию
числа->Вставить(1, 15); // Вставляем 15 в индекс 1

//Удалить указанное значение
nums->Удалить(20);

//Удалить указанный индекс
nums->RemoveAt(0);

// Получаем элементы
целое значение = числа [1]; // Получаем элемент с индексом 1

// Проверяем, содержит ли он
если (числа->Содержит(30))
    Console::WriteLine("Найдено 30");

// Очистить все элементы
числа->Очистить();

Список обхода

// для цикла
for (int i = 0; i < nums->Count; i++)
{
    Console::WriteLine("{0}й элемент: {1}", i, nums[i]);
}

// для каждого цикла
для каждого (int n в числах)
{
    Консоль::WriteLine(n);
}

Общие свойства и методы

Свойства/методыиллюстрировать
CountТекущее количество элементов
CapacityВнутренняя емкость (может увеличиваться автоматически)
Add(item)добавить элемент
AddRange(collection)Объединить все элементы другой коллекции
Insert(index, item)Вставить элемент в указанную позицию
Remove(item)Удаляет указанное значение (найдено первое совпадение)
RemoveAt(index)Удалить элемент по указанному индексу
Clear()Удалить все элементы
Contains(item)Проверьте, содержится ли указанное значение
IndexOf(item)Возвращает индекс элемента (-1, если он не существует)
Sort()Сортировочные элементы (для сопоставимых типов)
Reverse()Обратный порядок элементов

Пример вывода

10
15
30


.NET Dictionary

Основное введение

В .NET (такие языки, как C++/CLI, C#, VB.NET и т. д.)Dictionary<TKey, TValue>это универсальная коллекция, Используется для хранения «пар ключ-значение». Каждый ключ должен быть уникальным, а значение должно повторяться. оно принадлежитSystem::Collections::Genericпространство имен.

Объявление и инициализация

//Импортируем пространство имен
использование пространства имен System;
используя пространство имен System::Collections::Generic;

интервал основной()
{
    //Создаем словарь с ключом в виде целого числа и значением в виде строки
    Dictionary<int, String^>^users = gcnew Dictionary<int, String^>();

    // Прямая инициализация
    пользователи->Добавить(1, «Алиса»);
    пользователи->Добавить(2, «Боб»);
    пользователи->Добавить(3, «Чарли»);

    вернуть 0;
}

Добавляйте, изменяйте, удаляйте элементы и получайте доступ к ним

Dictionary<String^, int>^ age = gcnew Dictionary<String^, int>();

//Add new element
возраста->Добавить("Том", 25);
возраста->Добавить("Джерри", 30);

//Изменить значение (через индексатор)
возраст["Том"] = 26;

// Добавить или изменить (если ключ не существует, он будет добавлен автоматически)
возраст["Спайк"] = 40;

// удаляем элемент
age->Remove("Джерри");

//Доступ к элементам
if (возраст->ContainsKey("Том"))
{
    Console::WriteLine("Возраст Тома — {0}", ages["Том"]);
}

// Попытаемся получить значение (избегаем исключений)
средний возраст;
if (возраст->TryGetValue("Spike", age))
{
    Console::WriteLine("Возраст Спайка — {0}", age);
}

Траверсный словарь

Dictionary<int, String^>^users = gcnew Dictionary<int, String^>();
пользователи->Добавить(1, «Алиса»);
пользователи->Добавить(2, «Боб»);
пользователи->Добавить(3, «Чарли»);

// Используем цикл KeyValuePair
для каждой (KeyValuePair<int, String^> записи в пользователях)
{
    Console::WriteLine("Ключ = {0}, Значение = {1}", запись.Ключ, запись.Значение);
}

//Получаем только ключи
для каждого (ключ int в Users->Keys)
{
    Console::WriteLine("Ключ: {0}", key);
}

//Получаем только значения
для каждого (имя String^ в пользователях->Значения)
{
    Console::WriteLine("Имя: {0}", name);
}

Общие свойства и методы

Свойства/методыиллюстрировать
Add(key, value)Добавьте новый набор пар ключ-значение.
Remove(key)Удаляет указанный ключ и соответствующее ему значение.
Clear()Очистить все проекты
ContainsKey(key)Проверьте, существует ли указанный ключ
ContainsValue(value)Проверьте, существует ли указанное значение
TryGetValue(key, out value)Получите значение безопасно, не создавая исключения
CountТекущее количество элементов в словаре
KeysВозвращает коллекцию всех ключей
ValuesВозвращает коллекцию всех значений

Пример вывода

Ключ = 1, Значение = Алиса
Ключ = 2, Значение = Боб
Ключ = 3, Значение = Чарли
Тому 26 лет
Спайку 40 лет.


Получите последний файл в каталоге, используя .NET C++.

Введение

В .NET C++ вы можете использоватьSystem::IOПространства имен предоставляют функции для управления файлами и каталогами. Получить последние файлы в каталоге можно, прочитав всю информацию о файле и сравнив дату последнего изменения.

Пример кода

Вот полный пример, показывающий, как получить последний файл в указанном каталоге:

#include "stdafx.h"
#include <iostream>
#include <cliext/vector>
#include <System.IO>

использование пространства имен System;
используя пространство имен System::IO;
использование пространства имен cliext;

интервал основной()
{
    попробуй
    {
        //Указываем путь к каталогу
        String^directoryPath = "C:\\Ваш\\Каталог\\Путь";

        // Проверяем, существует ли каталог
        если (!Directory::Exists(directoryPath))
        {
            Console::WriteLine("Каталог не существует: {0}", DirectoryPath);
            вернуть -1;
        }

        // Получаем все файлы в каталоге
        массив^ файлы = Directory::GetFiles(directoryPath);

        // Если в каталоге нет файлов
        если (файлы->Длина == 0)
        {
            Console::WriteLine("В каталоге нет файла.");
            вернуть 0;
        }

        // Находим последний файл
        Строка ^ newestFile = nullptr;
        DateTime newestTime = DateTime::MinValue;

        для каждого (файл String^ в файлах)
        {
            // Получаем время последней модификации файла
            DateTime LastWriteTime = File::GetLastWriteTime(файл);

            // Сравниваем время и обновляем последнюю информацию о файле
            если (lastWriteTime > newestTime)
            {
                newestTime = LastWriteTime;
                новыйФайл = файл;
            }
        }

        // Вывод последней информации о файле
        Console::WriteLine("Последний файл: {0}", newestFile);
        Console::WriteLine("Время последнего изменения: {0}", newestTime);
    }
    поймать (Исключение^ ex)
    {
        Console::WriteLine("Произошла ошибка: {0}", ex->Message);
    }

    вернуть 0;
}

Объяснение кода

Сценарии применения

На что следует обратить внимание



.NET: System.Reflection

1. Что такое System.Reflection?

System.ReflectionЭто пространство имен в .NET Framework, которое предоставляет инструменты для проверки метаданных и управления ими, позволяя разработчикам динамически проверять типы, методы, свойства и т. д. во время выполнения, а также динамически создавать объекты и манипулировать ими.

2. Назначение System.Reflection

3. Общие категории System.Reflection

4. Примеры использования

Ниже приводится использованиеSystem.ReflectionПример программы, показывающий, как динамически проверять типы и методы, а также вызывать методы.

//Определяем простой пример класса
общественный класс SampleClass {
    публичная строка SayHello (имя строки) {
        return $"Привет, {имя}!";
    }
}

// Используем Reflection для динамического вызова методов
использование системы;
использование System.Reflection;

класс Программа {
    статическая пустота Main() {
        //Создаем экземпляр класса SampleClass
        Введите sampleType = typeof(SampleClass);
        объект sampleInstance = Activator.CreateInstance(sampleType);

        // Получаем информацию о методе SayHello
        MethodInfo MethodInfo = sampleType.GetMethod("SayHello");

        // Динамический вызов метода SayHello
        результат объекта = методInfo.Invoke(sampleInstance, новый объект [] { "Мир" });
        Console.WriteLine(результат); // Вывод: Привет, мир!
    }
}

В приведенном выше примере мы используемActivator.CreateInstanceчтобы создать экземпляр категории и использоватьMethodInfo.Invokeвызвать методSayHello

5. Общие сценарии применения



System::Management::ManagementClass

использовать

System::Management::ManagementClassЭто один из классов .NET Framework, обеспечивающий работу с инструментарием управления Windows (WMI). Он позволяет разработчикам читать, манипулировать и управлять системной информацией, такой как оборудование, операционная система, настройки сети и т. д.

пространство имен

System::ManagementнаходитсяSystem.Management.dllПрежде чем использовать эту функцию, вам необходимо добавить ссылку в пространство имен.

Обычное использование

Простой пример

// Метод написания C++/CLI
использование пространства имен System;
используя пространство имен System::Management;

интервал основной() {
    попробуй {
        ManagementClass^ mc = gcnew ManagementClass("Win32_OperatingSystem");
        для каждого (ManagementObject^ mo in mc->GetInstances()) {
            Console::WriteLine("Имя ОС: {0}", mo["Caption"]);
        }
    }
    поймать (Исключение^ ex) {
        Console::WriteLine("Ошибка: {0}", ex->Message);
    }
    вернуть 0;
}

Распространенные ошибки и их устранение

Связанные категории



Исправить ошибку недопустимого класса в Win32_NetworkAdapterConfiguration.

Описание кода ошибки

Код ошибки `0x80041010` возникает при использовании `System::Management::ManagementClass("Win32_NetworkAdapterConfiguration")`, что указывает на то, что WMI не может найти класс, что обычно означает:

Этапы решения

Вот полное исправление для воссоздания репозитория WMI для Windows 10/11:
net stop winmgmt
winmgmt /resetrepository
net start winmgmt

иллюстрировать

Проверка после ремонта

Чтобы подтвердить успешность восстановления, вы можете использовать любой из следующих методов:Способ 1: запрос PowerShell
Get-WmiObject Win32_NetworkAdapterConfiguration
Способ 2. Инструмент тестирования WMI (wbemtest).
  1. Нажмите Win+R и введитеwbemtest
  2. Нажмите «Подключиться» и введитеroot\cimv2и подключить
  3. Нажмите «Запросить» и введитеSELECT * FROM Win32_NetworkAdapterConfiguration

Дополнительные предложения



.NET C++ читает таблицу HTML

Обзор

В проектах .NET C++ (или, чаще, C++/CLI) чтение и анализ HTML-таблиц в файлах обычно требует использования внешней библиотеки анализа HTML, поскольку стандартная платформа .NET и сам C++ не имеют встроенных мощных возможностей анализа HTML DOM. Распространенный и эффективный вариант — использовать библиотеку на C# или другом языке .NET, а затем ссылаться на нее в C++/CLI.

Для проектов C++/CLI наиболее практичным и рекомендуемым подходом является использованиеHtml Agility Pack(HAP), очень популярный анализатор HTML .NET. Хотя HAP написан на C#, его можно легко использовать в качестве справочника на любом языке .NET, включая C++/CLI.

Шаги по использованию Html Agility Pack

  1. Создайте проект C++/CLI:Убедитесь, что ваш проект является проектом C++/CLI (например: консольное приложение CLR или приложение Windows Forms).
  2. Получите пакет Html Agility Pack:
  3. Реализация программного кода (C++/CLI):

Пример кода

Ниже приведен фрагмент кода C++/CLI, демонстрирующий, как читать локальный файл HTML и анализировать первую таблицу в нем (<table>) содержание:

#using <System.dll>
#using <System.Xml.dll>
#using <HtmlAgilityPack.dll> // Убедитесь, что ссылка включена

использование пространства имен System;
используя пространство имен System::IO;
использование пространства имен HtmlAgilityPack;

void ParseHtmlTable (String^ filePath)
{
    // Проверяем, существует ли файл
    если (!File::Exists(filePath))
    {
        Console::WriteLine("Ошибка: файл не существует.");
        возвращаться;
    }

    // 1. Загрузите HTML-файл
    HtmlDocument^ doc = gcnew HtmlDocument();
    попробуй
    {
        //Загружаем HTML из файла
        документ-> Загрузить (путь к файлу);
    }
    поймать (Исключение^ ex)
    {
        Console::WriteLine("Произошла ошибка при загрузке файла: " + ex->Message);
        возвращаться;
    }

    // 2. Выберите первую <table> узел
    HtmlNode^ table = doc->DocumentNode->SelectSingleNode("//table");

    если (таблица!= nullptr)
    {
        Console::WriteLine("Найдите таблицу HTML и начните анализ...");

        // 3. Выбрать все <tr> (столбец таблицы) узлы
        HtmlNodeCollection^ rows = table->SelectNodes(".//tr");

        если (строки!= nullptr)
        {
            для каждой (строки HtmlNode^ в строках)
            {
                // 4. Выберите <td> или <th> (ячейка) узел
                // Используйте | оператор для выбора <td> или <th>
                HtmlNodeCollection^ cell = row->SelectNodes("td | th");

                если (ячейки!= nullptr)
                {
                    Строка^ rowData = "";
                    для каждой (ячейки HtmlNode^ в ячейках)
                    {
                        // Получаем внутренний текст ячейки и удаляем начальные и конечные пробелы
                        rowData += cell->InnerText->Trim() + "\t";
                    }
                    Консоль::WriteLine(rowData);
                }
            }
        }
        еще
        {
            Console::WriteLine("Тег<tr> не найден в таблице.");
        }
    }
    еще
    {
        Console::WriteLine("Тег<table> не найден в файле.");
    }
}

int main(array<String^>^ args)
{
    // Пожалуйста, замените "your_html_file.html" фактическим путем к файлу.
    String^ htmlFilePath = "C:\\путь\\к\\ваш_html_файл.html";
    ParseHtmlTable(htmlFilePath);
    вернуть 0;
}

Объяснение основных технических моментов



HTTP API-сервер .NET C++

Использование класса HttpListener

HttpListenerКласс позволяет создать простой автономный HTTP-сервер в приложении C++/CLI.

Требования и настройки проекта

Создайте проект C++/CLI в Visual Studio (например, консольное приложение CLR) и убедитесь, что на него есть ссылка.System.dll

Пример кода

Используется следующееHttpListenerКод C++/CLI для создания простого сервера API и обработки запросов GET.

#using <System.dll>

использование пространства имен System;
используя пространство имен System::Net;
используя пространство имен System::Threading::Tasks;
используя пространство имен System::Text;

// Функция для обработки входящих HTTP-запросов
void HandleRequest (контекст HttpListenerContext^)
{
    HttpListenerRequest^ запрос = контекст->Запрос;
    HttpListenerResponse^ ответ = контекст->Ответ;

    //Настройки ответа по умолчанию
    ответ->ContentType = "application/json; charset=utf-8";
    String^ responseString = "";
    ответ->Код состояния = 200; // По умолчанию — ОК

    // Проверяем запрошенный путь и метод
    String^ URL = request->RawUrl->ToLower();
    
    if (url == "/api/status" && request->HttpMethod == "GET")
    {
        // Обработка запроса GET /api/status
        responseString = "{\"status\": \"Сервер работает\", \"language\": \"C++/CLI\"}";
    }
    еще
    {
        // Обработка других неопределенных запросов
        ответ->Код состояния = 404; // Не найдено
        responseString = "{\"ошибка\": \"404 не найден\", \"путь\": \"" + URL + "\"}";
    }

    // Преобразуем строковый ответ в байты и записываем в выходной поток
    array<Byte>^ buffer = Кодировка::UTF8->GetBytes(responseString);
    ответ->ContentLength64 = буфер->Длина;
    
    попробуй
    {
        ответ->OutputStream->Write(буфер, 0, буфер->Длина);
        ответ->Выходной поток->Закрыть();
    }
    поймать (Исключение^)
    {
        // Игнорируем ошибки записи, обычно клиент отключается
    }
}

// Функция для запуска HttpListener
void StartListener (префикс String^)
{
    HttpListener^ прослушиватель = gcnew HttpListener();
    
    попробуй
    {
        прослушиватель->Префиксы->Добавить(префикс);
        слушатель-> Старт();
        Console::WriteLine("Сервер C++/CLI API запущен, прослушивает: {0}", префикс);
    }
    поймать (Исключение^ ex)
    {
        Console::WriteLine("Произошла ошибка при запуске HttpListener. Пожалуйста, подтвердите, занято ли разрешение или адрес.");
        Console::WriteLine("Сообщение об ошибке: {0}", ex->Message);
        возвращаться;
    }

    // Асинхронный цикл, продолжаем получать запросы
    в то время как (слушатель->IsListening)
    {
        попробуй
        {
            // Синхронно ждем следующего запроса
            HttpListenerContext^ context = прослушиватель->GetContext();
            
            // Используйте Task для асинхронной обработки запросов, чтобы избежать блокировки циклов прослушивания
            Task::Factory->StartNew(gcnew Action<HttpListenerContext^>(&HandleRequest), context);
        }
        поймать (Исключение^)
        {
            // Исключение будет выброшено, когда прослушиватель остановлен
            перерыв;
        }
    }
    
    если (слушатель->IsListening)
    {
        слушатель->Закрыть();
    }
}

int main(array<String^>^ args)
{
    //Устанавливаем префикс URL для мониторинга
    // Примечание. В Windows могут потребоваться права администратора или использование netsh для регистрации пространства имен URL.
    String^ ListenPrefix = "http://localhost:8080/";
    StartListener (префикс прослушивания);
    
    Console::WriteLine("Нажмите любую клавишу, чтобы завершить работу сервера...");
    Консоль::ReadKey(истина);
    
    вернуть 0;
}

Примечание о разрешениях на выполнение

В системах Windows, если код запускается без прав администратора,HttpListenerИсключение отказа в доступе может быть выдано при прослушивании определенного порта. Вы можете зарегистрировать пространство имен URL-адресов, чтобы разрешить учетным записям, не являющимся администраторами, запускать сервер с помощью следующей команды:


netsh http add urlacl url=http://+:8080/ user=Everyone

вhttp://+:8080/следует заменить фактическим префиксом, используемым в вашем коде.



ASP.NET Core C# HTTP API-сервер

Обзор

В ASP.NET Core стандартным типом проекта для создания сервера HTTP API является веб-API. Рекомендуется последняя версия .NET (.NET 6 и выше).Minimal APIsдля быстрого и легкого создания конечных точек API.

Минимальные API объединяют логику запуска (Запуск) и определения маршрутизации (Маршрутизация) в один файл с использованием минимального количества файлов и строк кода.Program.csсередина.

Настройка проекта (с помощью CLI)

Чтобы создать новый проект минимального API, вы можете использовать .NET CLI (интерфейс командной строки):


dotnet new web -n MinimalApiServer
cd MinimalApiServer

Пример кода программы: Program.cs

Ниже приведен основной файл сервера Minimal API.Program.csполное содержание. Он определяет запуск сервера, промежуточное программное обеспечение и две конечные точки API (одну GET, одну POST).

// 1. Создайте экземпляр веб-приложения (заменив традиционную категорию «Автозагрузка»)
вар строитель = WebApplication.CreateBuilder(args);

// 2. Регистрация службы (Конфигурация службы)
//Здесь можно добавить подключение к базе данных, сервисы проверки и т.д.
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(); // Включаем поддержку Swagger/OpenAPI

вар приложение = builder.Build();

// 3. Настройка конвейера HTTP-запросов (конфигурация промежуточного программного обеспечения)

// В среде разработки включаем Swagger UI для тестирования
если (app.Environment.IsDevelopment())
{
    приложение.UseSwagger();
    app.UseSwaggerUI();
}

// Включить перенаправление HTTPS (рекомендуется для производственных сред)
// app.UseHttpsRedirection();

// 4. Определить конечную точку API (Определение конечной точки)
// Класс сущности, используемый для POST-запросов
общедоступная запись Product (int Id, строковое имя, десятичная цена);

// Конечная точка запроса GET: /api/hello
app.MapGet("/api/hello", () =>
{
    returnResults.Ok(new { message = "Привет от ASP.NET Core Minimal API!", timestamp = DateTime.Now });
});

// Конечная точка запроса GET: /api/products/{id}
app.MapGet("/api/products/{id}", (int id) =>
{
    // Предположим, запрашиваем продукты из базы данных
    если (идентификатор == 1)
    {
        returnResults.Ok(новый продукт(1, "ноутбук", 1200,00 м));
    }
    returnResults.NotFound(new { error = $"Идентификатор продукта {id} не найден" });
});

// Конечная точка POST-запроса: /api/products
app.MapPost("/api/products", (Продукт) =>
{
    // ASP.NET Core автоматически десериализует тело запроса JSON в сущность Product
    Console.WriteLine($"Получен новый товар: {product.Name} (ID: {product.Id})");
    
    //В реальном приложении здесь будет выполняться операция добавления базы данных.
    //Вернем 201 Код статуса создан
    returnResults.Created($"/api/products/{product.Id}", product);
});

// 5. Запускаем приложение и начинаем прослушивать HTTP-запросы
приложение.Выполнить();

Выполнение и тестирование

  1. Запустите сервер:Выполнить в каталоге проектаdotnet run
  2. Конечная точка теста:Серверы обычно по умолчанию настроены наhttp://localhost:5000илиhttps://localhost:7000бегать.

Ключевые технические моменты



Как запускать программы .NET на Chromebook

1. Запустите .NET, используя поддержку Chromebook Linux.

Большинство современных Chromebook поддерживают запуск приложений Linux через проект Crostini.

  1. Включите функции Linux:
  2. Установите .NET SDK:
  3. Скомпилируйте и запустите приложение .NET в терминале.

2. Используйте облачную среду разработки.

Вы можете использовать облачную платформу для запуска программ .NET на Chromebook без установки какого-либо программного обеспечения локально.

3. Используйте контейнеры Docker для запуска .NET.

Chromebook поддерживает использование контейнеров для запуска приложений, и вы можете запустить среду .NET через Docker.

  1. Установите Docker в среде Linux.
  2. Загрузите образ контейнера .NET:
    docker pull mcr.microsoft.com/dotnet/runtime:7.0
  3. Запустите программу .NET внутри контейнера.

4. Используйте кроссплатформенную поддержку

Начиная с .NET 6, приложения могут работать на нескольких платформах, включая Linux. Скомпилируйте свое приложение в кроссплатформенный формат и разверните его на Chromebook.

  1. Скомпилируйте приложение на любой машине:
    dotnet publish -c Release -r linux-x64 --self-contained
  2. Перенесите скомпилированный архив на свой Chromebook и запустите его, используя включенную среду выполнения.

На что следует обратить внимание



Программирование пользовательского интерфейса .NET

Основная технология

Простой пример WinForms

использование системы;
использование System.Windows.Forms;

открытый класс MyForm: Форма {
    публичная MyForm() {
        Кнопка btn = новая кнопка();
        btn.Text = "Нажмите на меня";
        btn.Click += (s, e) => MessageBox.Show("Вы нажали кнопку!");
        Controls.Add(BTN);
    }

    [STAThread]
    статическая пустота Main() {
        Приложение.EnableVisualStyles();
        Application.Run(новый MyForm());
    }
}

Простой пример WPF (XAML + C#)

// MainWindow.xaml
<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        Title="Пример WPF" Height="200" Width="300">
    <StackPanel>
        <Button Content="Нажмите на меня" Click="Button_Click"/>
    </StackPanel>
</Окно>

// MainWindow.xaml.cs
частный void Button_Click (отправитель объекта, RoutedEventArgs e) {
    MessageBox.Show("Вы нажали кнопку!");
}

Простой пример MAUI

// Главная страница.xaml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             x:Class="MyApp.MainPage">
    <VerticalStackLayout Spacing="25" Padding="30">
        <Button Text="Нажмите на меня" Clicked="OnButtonClicked"/>
    </VerticalStackLayout>
</ContentPage>

// Главная страница.xaml.cs
Private void OnButtonClicked (отправитель объекта, EventArgs e) {
    await DisplayAlert("Уведомление", "Вы нажали кнопку!", "ОК");
}

Консультации по выбору технологии пользовательского интерфейса

технология использовать платформа
WinForms Быстрая разработка настольных приложений Windows
WPF Сложные настольные приложения, шаблон MVVM Windows
MAUI Кроссплатформенное приложение Windows、macOS、Android、iOS
Blazor Веб-интерфейсная разработка Кроссплатформенный (Интернет)


.NET обходит дочерние элементы управления для пакетного изменения свойств.

Следующие примеры показывают, как использоватьControls->GetEnumerator()Метод для перебора всех дочерних элементов управления в форме .NET один за другим и пакетного изменения их свойств.

Пример кода:

использование системы;
использование System.Drawing;
использование System.Windows.Forms;

общедоступный класс FormExample: Форма
{
    публичный пример формы()
    {
        //Инициализируем некоторые элементы управления
        Кнопка button1 = новая кнопка { Text = «Кнопка 1», Location = новая точка (10, 10) };
        TextBox textBox1 = новый TextBox {Местоположение = новая точка (10, 50)};
        
        Controls.Add(button1);
        Controls.Add(textBox1);

        // Используйте GetEnumerator() для перемещения и изменения свойств элемента управления
        МодифицироватьЭлементы управления();
    }

    частная пустота ModifyControls()
    {
        вар перечислитель = Controls.GetEnumerator();
        
        в то время как (перечислитель.MoveNext())
        {
            Управление control = (Control)enumerator.Current;

            // Пример настройки: установить голубой цвет фона всех элементов управления
            control.BackColor = Color.LightBlue;
            
            // Если элементом управления является TextBox, сделайте его недоступным для редактирования
            if (элемент управления — TextBox)
            {
                control.Enabled = ложь;
            }
        }
    }
    
    //Запускаем приложение
    общественная статическая пустота Main()
    {
        Application.Run(новый FormExample());
    }
}

иллюстрировать

**GetEnumerator()**: используйте метод Controls.GetEnumerator(), чтобы получить перечислитель элемента управления, чтобы можно было просмотреть все дочерние элементы управления.

**Условное изменение**. В цикле while измените свойства каждого объекта Control, например, установив светло-голубой цвет фона, и внесите определенные изменения в зависимости от типа элемента управления, например, установив для TextBox значение нередактируемого.

**Использование**. Этот метод очень эффективен, когда вам нужно изменить свойства в пакетном режиме, например настроить стиль пользовательского интерфейса или отключить несколько элементов управления при определенных обстоятельствах.

Эффект казни

После выполнения цвет фона всех элементов управления изменится на светло-синий, а все элементы управления TextBox станут недоступными для редактирования.



.NET MessageBox

1. Базовый синтаксис и распространенная перегрузка

В С++/CLIMessageBox::ShowЭто статический метод, используемый для открытия диалогового окна. Самый полный метод вызова включает в себя: содержание сообщения, заголовок, тип кнопки, значок и кнопку по умолчанию.

используя пространство имен System::Windows::Forms;

//Простейшее отображение
MessageBox::Show("Операция завершена");

//Полная версия параметра
Результат DialogResult = MessageBox::Show(
    «Вы уверены, что хотите удалить этот файл?», // Содержимое сообщения (Текст)
    "Подтвердить удаление", // заголовок окна (Caption)
    MessageBoxButtons::ДаНет, // Комбинация кнопок
    MessageBoxIcon::Warning, // Значок подсказки
    MessageBoxDefaultButton::Button2 // Фокус по умолчанию находится на второй кнопке (Нет)
);

2. Обработка возвращаемого значения (DialogResult).

Необходимо установить флажок, если кнопка содержит несколько вариантов, например «Да/Нет» или «ОК/Отмена».DialogResultчтобы определить последующую логику.

if (результат == DialogResult::Да) {
    //Выполняем логику удаления
} еще {
    //Отменяем операцию
}

3. Справочник по общим значениям перечисления

категория Общие варианты иллюстрировать
MessageBoxButtons OK, OKCancel, YesNo, YesNoCancel Определяет, какие кнопки отображаются под диалоговым окном.
MessageBoxIcon Information, Warning, Error, Question Определите значок и системные звуковые эффекты, отображаемые слева.
DialogResult OK, Cancel, Yes, No ShowВозвращаемое значение метода показывает, какую клавишу нажал пользователь.

4. Продвинутая техника: объедините std::string или float.

потому чтоMessageBox::ShowПолучено .NETSystem::String^, если у вас есть собственные данные C++, вам необходимо их преобразовать.

#include <строка>
#include <msclr/marshal_cppstd.h>

// Отображение std::string в MessageBox
std::string cpp_str = "Ошибка ядра";
MessageBox::Show(msclr::interop::marshal_as<System::String^>(cpp_str));

//Форматируем число с плавающей запятой и отображаем его
плавающее расстояние = 1,23456f;
MessageBox::Show("Расстояние: " + dist.ToString("F2")); // Отображение «Расстояние: 1,23»

Распространенные ошибки и решения



Диалоговое окно пользовательского ввода .NET

1. Почему MessageBox не поддерживает поле редактирования?

System::Windows::Forms::MessageBoxпредназначен для отображения «уведомлений» или «предупреждений» и получения решений пользователя с помощью стандартных кнопок. Он не имеет функции динамической вставки элементов управления. Если вам нужно ввести текст, стандартным подходом является наследованиеSystem::Windows::Forms::FormСоздайте собственное диалоговое окно.


2. Реализация пользовательского поля ввода.

Это простая категория многократного использования, содержащая метки, текстовые поля и кнопки ОК.

использование пространства имен System;
используя пространство имен System::Windows::Forms;
используя пространство имен System::Drawing;

публичный класс ссылки CustomInputBox: общественная форма {
публика:
    Текстовое поле^ txtInput;
    Кнопка^ btnOK;
    Метка^ lblPrompt;

    CustomInputBox (заголовок String^, приглашение String^) {
        //Установим основные свойства формы
        это->Текст = заголовок;
        это->Размер = Система::Рисование::Размер(300, 150);
        this->FormBorderStyle = System::Windows::Forms::FormBorderStyle::FixedDialog;
        это->StartPosition = FormStartPosition::CenterParent;
        это->MaximizeBox = ложь;
        это->MinimizeBox = ложь;

        // Текст подсказки
        lblPrompt = gcnew Label();
        lblPrompt->Текст = подсказка;
        lblPrompt->Местоположение = Точка(15, ​​15);
        lblPrompt->Size = System::Drawing::Size(250, 20);

        // поле ввода
        txtInput = gcnew TextBox();
        txtInput->Location = Point(15, 40);
        txtInput->Size = System::Drawing::Size(250, 20);

        // кнопка ОК
        btnOK = gcnew Button();
        btnOK->Текст = «ОК»;
        btnOK->DialogResult = System::Windows::Forms::DialogResult::OK;
        btnOK->Местоположение = Точка(190, 75);
        
        // Добавляем элементы управления
        это->Управление->Добавить(lblPrompt);
        это->Управление->Добавить(txtInput);
        это->Управление->Добавить(btnOK);
        
        //Установим кнопку по умолчанию (нажатие Enter эквивалентно нажатию OK)
        это->AcceptButton = btnOK;
    }
};

3. Как вызвать в основной программе

использоватьShowDialog()Метод открывает окно в режиме принудительного ответа и проверяет, является ли возвращаемое значениеOK

недействительный OnButtonClick () {
    CustomInputBox^ ibox = gcnew CustomInputBox("Системный ввод", "Введите имя файла:");
    
    if (ibox->ShowDialog() == System::Windows::Forms::DialogResult::OK) {
        String^ userInput = ibox->txtInput->Text;
        
        if (!String::IsNullOrWhiteSpace(userInput)) {
            // Обрабатываем строку, введенную пользователем
            MessageBox::Show("Вы ввели: " + userInput);
        }
    }
}

4. Сравнение планов

план Сложность реализации преимущество недостаток
Пользовательская форма середина Полный контроль над макетом и возможность добавлять логику проверки (например, ограниченное количество). Необходимо написать больше кода интерфейса.
VB Interaction Низкий Одна строка кода завершена. Нужно процитироватьMicrosoft.VisualBasic, стиль фиксирован и не может быть изменен.
Win32 API высокий Среда .NET не требуется. Разработка на C++/CLI чрезвычайно тривиальна, и ее сложно поддерживать.

Распространенные ловушки



Ошибка красного креста PictureBox

Описание явления

PictureBox отображает красный крестик, указывающий, чтоOnPaintВо время выполнения произошло исключение, Однако .NET по умолчанию поглощает это исключение и не отображает его в общем журнале или точках прерывания, что затрудняет отладку.

Распространенные причины

причина иллюстрировать
Растровое изображение было удалено PictureBox по-прежнему содержит ссылку на растровое изображение, а доступ к освобожденным ресурсам осуществляется во время Paint.
Исходный поток закрыт После использования Stream для создания растрового изображения и последующего закрытия потока данные растрового изображения станут недействительными.
Межпоточный доступ Фоновый поток напрямую изменяет или назначает Bitmap, что конфликтует с потоком пользовательского интерфейса.
Исчерпание ресурсов GDI+ Большое количество растровых изображений не удаляется, что приводит к нехватке памяти GDI+.

Сравнение сообщений об ошибках

Сообщение об исключении первопричина Направление решения
ArgumentException: Parameter is not valid Растровое изображение было удалено или поток был закрыт. Глубокая копия Bitmap, не зависит от Stream
InvalidOperationException: object is in use elsewhere Доступ к одному и тому же растровому изображению одновременно в разных потоках Добавьте блокировку или вернитесь к потоку пользовательского интерфейса.
OutOfMemoryException Ресурсы GDI+ исчерпаны или формат изображения не поддерживается. Немедленно утилизируйте старое растровое изображение и проверьте источник изображения.
ExternalException: A generic error occurred in GDI+ Файл или поток, соответствующий растровому изображению, больше не существует. Убедитесь, что источник по-прежнему действителен в течение срока службы растрового изображения.

Шаги отладки

Поскольку проблема с красным крестом может возникать не часто, рекомендуется действовать по порядку:

  1. Перехват исключений OnPaint: Наследовать PictureBox, переопределитьOnPaint, Используйте try/catch для перехвата исключений и вывода их вDebug.WriteLine, выполнить после захватаthis.Image = nullИзбегайте постоянных красных крестов.
  2. Подтвердить сообщение об исключении: обратите внимание на сообщение в окне вывода Visual Studio. Проверьте таблицу сообщений об ошибках выше, чтобы найти основную причину.
  3. Отслеживание жизненного цикла растрового изображения: каждый раз, когда назначается Dispose Bitmap, записьEnvironment.StackTrace, узнать, кто и в какой момент времени выпустил ресурс.
  4. Запретить удаление растрового изображения, которое все еще используется: Проверьте перед утилизацией Содержит ли PictureBox растровое изображение, если да, то сначалаImageУстановите значение null, а затем Dispose.

Безопасный режим смены изображения

Все операции по смене изображений выполняются в этом режиме, чтобы обеспечить потокобезопасность и правильный порядок Dispose:

// С++/CLI
void SafeUpdateImage(Bitmap^ newBmp) {
    если (pictureBox1->InvokeRequired) {
        PictureBox1->Invoke(gcnew Action<Bitmap^>(
            это, &YourClass::SafeUpdateImage), newBmp);
        возвращаться;
    }
    Bitmap^ old = Safe_cast<Bitmap^>(pictureBox1->Image);
    PictureBox1-> Изображение = newBmp; // Сначала переходим к новому изображению
    if (old != nullptr) old->Dispose(); // Снова утилизируем старую картинку
}

Правильный способ создания растрового изображения из потока

// Ошибка: растровое изображение недействительно после закрытия потока
используя (var поток = новый FileStream (путь, FileMode.Open)) {
    PictureBox1.Image = новое растровое изображение (поток);
}

// Правильно: глубокое копирование, не полагается на поток
Растровое изображение загружено;
используя (varstream = новый FileStream(path, FileMode.Open))
используя (var tmp = новое растровое изображение (поток)) {
    загружено = новое растровое изображение (tmp);
}
PictureBox1.Image = загружено;

Контрольный список устранения неполадок

Проверить элементы Способ подтверждения
Размещено ли растровое изображение в другом месте Унифицированно меняйте изображения с помощью SafeUpdateImage и записывайте StackTrace.
Закрыт ли исходный поток Вместо этого используйте глубокое копированиеnew Bitmap(tmp)
Следует ли осуществлять доступ через потоки Проверьте InvokeRequired при изменении изображений, всегда вызывайте обратно в поток выполнения пользовательского интерфейса.
Красный крест появляется сразу или через некоторое время? Если оно появляется сразу, это может быть источником проблемы; через некоторое время это может быть проблема с синхронизацией Dispose.


Получите содержимое сообщения m в .NET StackTrace.

Предыстория проблемы

Когда в трассировке стека появляется следующее сообщение об ошибке, разработчикам может потребоваться проверитьMessageСодержимое объекта:

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

В это время необходимо перезаписать или обнаружитьWndProcв методеMessage mчтобы проверить его детали.

Решение

Вот несколько способов проверить или записатьMessageСодержимое объекта.

1. Переопределить метод WndProc.

Если у вас есть доступ к исходному коду формы или элемента управления, рекомендуется перезаписатьWndProcметод, прямая запись или проверкаMessage mсодержание.

защищенное переопределение void WndProc (ref Message m)
{
    попробуй
    {
        //Запись содержимого сообщения
        Console.WriteLine($"Сведения о сообщении: hWnd={m.HWnd}, Msg={m.Msg}, WParam={m.WParam}, LParam={m.LParam}");
        base.WndProc(ref m);
    }
    поймать (исключение ex)
    {
        Console.WriteLine($"Исключение: {ex}");
        бросать; // Сохраняем исходное поведение исключения
    }
}
    

Этот код будет регистрировать соответствующую информацию каждый раз при получении сообщения и позволит разработчику проанализировать ее дальше.

2. Добавьте обработку исключений в метод Dispose.

Если ошибка возникает вDisposeВ методе вы можете добавить обработку исключений внутри метода для проверки соответствующей информации.

защищенное переопределение void Dispose (удаление bool)
{
    попробуй
    {
        если (удаление)
        {
            // Освобождаем ресурсы
        }
        base.Dispose(удаление);
    }
    поймать (исключение ex)
    {
        Console.WriteLine($"Исключение в Dispose: {ex}");
        бросать;
    }
}
    

Это гарантирует, что важная информация об ошибках не будет проигнорирована при освобождении ресурсов.

3. Используйте глобальную обработку исключений

Если вы не можете определить, где произошла ошибка, вы можете использовать глобальную обработку исключений для записи трассировок стека и связанной информации.


AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
{
    Exception ex = (Exception)args.ExceptionObject;
    Console.WriteLine($"Unhandled Exception: {ex}");
    Environment.Exit(1);
};

    

4. Используйте инструмент отладки, чтобы проверить сообщение.

Вы можете использовать Visual Studio для установки точек останова вWndProcилиDisposeметод и проверкаMessageСодержимое объекта.

Ключевые свойства объекта Сообщение

На что следует обратить внимание



Переопределение Form.WmClose(Message& m) в .NET C++

Предыстория проблемы

В .NET C++/CLIWmClose(Message& m)даSystem.Windows.Forms.FormМетод внутренней защиты не может быть перезаписан напрямую. Однако можно переопределитьWndProcметод перехвата и обработкиWM_CLOSEсообщение для достижения аналогичного переопределенияWmCloseэффект.

Полный пример

#include <Windows.h>
#include <System.Windows.Forms.h>

использование пространства имен System;
используя пространство имен System::Windows::Forms;

общедоступный класс ссылки CustomForm: общедоступная форма
{
защищено:
    // Имитация переопределения поведения WmClose
    void WmClose(Сообщение% м)
    {
        //Добавляем сюда собственное поведение WM_CLOSE
        if (MessageBox::Show("Вы уверены, что хотите закрыть окно?", "Подтвердить", MessageBoxButtons::ДаНет) == DialogResult::Да)
        {
            // Продолжаем вызывать базовое поведение для корректного завершения работы
            this->Form::WndProc(m);
        }
        еще
        {
            // Предотвращаем закрытие окна
            возврат;
        }
    }

    // Переопределить WndProc, перехватить сообщение WM_CLOSE и вызвать WmClose
    переопределение виртуальной пустоты WndProc (Message% m)
    {
        const int WM_CLOSE = 0x0010;

        если (m.Msg == WM_CLOSE)
        {
            WmClose(м); // Вызов пользовательского метода WmClose
        }
        еще
        {
            // Обрабатываем другие сообщения
            Форма::WndProc(м);
        }
    }
};

[STAThread]
int main(array<String^>^ args)
{
    Приложение::EnableVisualStyles();
    Приложение::SetCompatibleTextRenderingDefault(false);

    CustomForm^ form = gcnew CustomForm();
    form->Text = "Пример переопределения поведения WmClose";
    Приложение::Выполнить(форма);

    вернуть 0;
}
    

Объяснение кода

1. Метод WmClose

имитировать перезаписьWmCloseЗдесь можно настроить поведение закрытия окна. Используйте диалоговое окно подтверждения, чтобы спросить пользователя, хотят ли они закрыть окно.

2. Переопределить WndProc

перезаписатьWndProcметод перехватаWM_CLOSEсообщение и делегировать его пользовательскомуWmCloseметод.

3. Продолжайте обрабатывать другие сообщения.

без перехватаWM_CLOSEВ случае вызова базового классаWndProcМетод обработки других сообщений.

На что следует обратить внимание



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

Инструкция по применению

InvokeRequiredдаSystem::Windows::Forms::ControlАтрибут используется для определения того, является ли текущий поток выполнения потоком выполнения пользовательского интерфейса, которому принадлежит элемент управления. При доступе к элементам управления пользовательского интерфейса из фонового потока вы должны использоватьInvokeМаршалируйте операцию обратно в поток пользовательского интерфейса.

Полный пример: установка изображений PictureBox

использование пространства имен System;
используя пространство имен System::Windows::Forms;
используя пространство имен System::Drawing;

ссылка на класс ImageHelper {
публика:
    static void SetImageSafe(PictureBox^ pPictureBox, Bitmap^ b) {
        если (pPictureBox == nullptr)
            возврат;

        если (pPictureBox->InvokeRequired) {
            // Используем MethodInvoker для вызова той же функции, но выполняем ее в потоке пользовательского интерфейса
            pPictureBox->Вызов(
                gcnew MethodInvoker (gcnew Action(ImageHelper::InvokeCallback), 
                gcnew Tuple(pPictureBox, б))
            );
        } еще {
            ApplyImage(pPictureBox, б);
        }
    }

частный:
    // Маршалированная функция-прокси
    static void InvokeCallback (состояние Object^) {
        Кортеж^ args = static_cast^>(состояние);
        ApplyImage(args->Item1, args->Item2);
    }

    // Фактическое выполнение установки логики изображения
    static void ApplyImage(PictureBox^ pPictureBox, Bitmap^ b) {
        попробуй {
            если (b != nullptr) {
                если (pPictureBox->Image!= nullptr)
                    удалить pPictureBox->Изображение;
                pPictureBox-> Изображение = б;
            }
        }
        catch (System::Exception^ ex) {
            Console::WriteLine("Не удалось установить изображение: " + ex->Message);
        }
    }
};

Сосредоточьтесь на организации



Защитите pPictureBox->Image = b, чтобы избежать сбоя программы.

Возможные причины аварии

Рекомендуемые методы безопасной настройки изображений


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, б });
    } еще {
        SetImageInternal (pPictureBox, б);
    }
}

void SetImageInvoker(Object^ sender, EventArgs^ e) {
    // Если эта версия не используется, ее можно игнорировать и сохранить как полную ссылку.
}

void SetImageInternal(PictureBox^ pPictureBox, Bitmap^ b) {
    попробуй {
        если (b != nullptr) {
            если (pPictureBox->Image!= nullptr)
                удалить pPictureBox->Изображение;

            pPictureBox-> Изображение = б;
        }
    }
    catch (System::Exception^ ex) {
        Console::WriteLine("Не удалось установить изображение: " + ex->Message);
    }
}

Упрощенная версия (рекомендуется анонимное делегирование)

Если вы просто хотите быстро защитить `pPictureBox->Image = b;`, следующий метод записи является более прямым и эффективным:
использование пространства имен System;
используя пространство имен System::Windows::Forms;
используя пространство имен System::Drawing;

void SafeAssignImage(PictureBox^ pPictureBox, Bitmap^ b) {
    если (pPictureBox->InvokeRequired) {
        pPictureBox->Invoke(gcnew MethodInvoker(gcnew делегат {
            попробуй {
                если (b != nullptr) {
                    если (pPictureBox->Image!= nullptr)
                        удалить pPictureBox->Изображение;
                    pPictureBox-> Изображение = б;
                }
            } catch (System::Exception^ ex) {
                Console::WriteLine("Не удалось установить изображение: " + ex->Message);
            }
        }));
    } еще {
        попробуй {
            если (b != nullptr) {
                если (pPictureBox->Image!= nullptr)
                    удалить pPictureBox->Изображение;
                pPictureBox-> Изображение = б;
            }
        } catch (System::Exception^ ex) {
            Console::WriteLine("Не удалось установить изображение: " + ex->Message);
        }
    }
}

Дополнительная информация



Создайте единую среду разработки языкового интерфейса с помощью Blazor.

Что такое Блазор?

Blazor — это интерфейсная платформа, запущенная Microsoft, которая позволяет разработчикам использовать синтаксис C# и Razor для создания интерактивных веб-приложений. Код C# можно выполнить в браузере без использования JavaScript.

Преимущества для разработчиков

Режим Блазора

Интеграция с .NET

Blazor легко интегрирует ASP.NET Core, может комбинироваться с существующими приложениями .NET и поддерживает внедрение зависимостей, маршрутизацию, архитектуру компонентов и другие функции.

Метод развертывания

краткое содержание

Для разработчиков, основной технологией которых является .NET, Blazor предоставляет решение, соответствующее унифицированному языку JavaScript Node.js, позволяющее как клиентской, так и внутренней части использовать C#, создавая более последовательный опыт разработки.



Создавайте кроссплатформенные приложения с использованием .NET MAUI.

Что такое .NET MAUI?

.NET MAUI (UI многоплатформенного приложения) — это кроссплатформенная платформа приложений, выпущенная Microsoft. Вы можете использовать C# и XAML для единоразового написания кода и его развертывания в Windows, Android, iOS и даже macOS.

Преимущества для разработчиков

Поддерживаемые платформы

Гибридный режим Blazor

Гибридный режим Blazor в .NET MAUI можно использовать для встраивания веб-интерфейса в собственные приложения, что позволяет разработчикам использовать компоненты Razor для разработки кроссплатформенных пользовательских интерфейсов, сохраняя при этом доступ к собственным API.

Интеграция и развертывание

краткое содержание

.NET MAUI на данный момент является наиболее полным кроссплатформенным решением .NET. Его можно объединить с Blazor для достижения унифицированной разработки приложений для веб-платформ, настольных и мобильных платформ, предоставляя разработчикам .NET полноценный опыт, согласованный по языку и технологиям.



Сравнение ASP.NET Core и .NET MAUI

позиция

Платформа поддержки

язык разработки

Пользовательский интерфейс и взаимодействие

Применимые сценарии

Интеграция

.NET MAUI можно интегрировать с серверными службами, созданными ASP.NET Core, посредством вызовов API, чтобы сформировать полную архитектуру «внешнее приложение + внутренний API».

Подвести итог

ASP.NET Core решает проблемы «внутренних служб» и «веб-приложений», а .NET MAUI фокусируется на «кроссплатформенных приложениях на стороне клиента». Эти два понятия не являются взаимоисключающими, но часто используются вместе для формирования комплексного решения.



Простой пример приложения .NET MAUI

Создайте базовое приложение .NET MAUI.

Ниже приведен пример кросс-платформенного приложения, созданного с использованием .NET MAUI, которое отображает на экране кнопку, обновляющую счетчик текста при нажатии.

Основной файл: 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="Вы еще не нажали кнопку"
               Размер шрифта="24"
               HorizontalOptions="Центр" />

        <Текст кнопки="Нажмите на меня"
                Clicked="OnCounterClicked"
                HorizontalOptions="Центр" />
    </VerticalStackLayout>
</ContentPage>

Внутренний код: MainPage.xaml.cs.

пространство именMauiApp;

общедоступный частичный класс MainPage: ContentPage
{
    число интервалов = 0;

    публичная главная страница()
    {
        ИнициализироватьКомпонент();
    }

    частная пустота OnCounterClicked (отправитель объекта, EventArgs e)
    {
        считать++;
        counterLabel.Text = $"Вы нажали {count} раз";
    }
}

Метод исполнения

краткое содержание

Этот простой пример демонстрирует кроссплатформенные возможности .NET MAUI. Разработчикам достаточно написать код XAML и C# только один раз, чтобы запустить его на нескольких платформах одновременно.



Создайте проект .NET MAUI в Visual Studio.

Шаг 1. Установите необходимые инструменты

Шаг 2. Создайте новый проект MAUI.

  1. Открыть Visual Studio
  2. Нажмите «Создать новый проект».
  3. Найдите и выберите ".NET MAUI App"Шаблон
  4. Нажмите «Далее» и введите название и местоположение проекта.
  5. Нажмите «Создать», чтобы завершить инициализацию проекта.

Шаг 3. Понимание структуры проекта

Шаг 4. Запустите приложение.

Дополнительная информация

краткое содержание

Для создания проекта .NET MAUI с помощью Visual Studio требуется всего несколько шагов, а один набор кода можно развернуть на нескольких платформах одновременно, что делает его идеальным выбором для современных полноценных разработчиков C#.



Примеры и пояснения MainPage.xaml

Пример: 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"
                         ВертикальныеОпции="Центр">

        <Ярлык
            Text="Добро пожаловать в .NET MAUI!"
            SemanticProperties.HeadingLevel="Level1"
            Размер Шрифта="32"
            HorizontalOptions="Центр" />

        <Кнопка
            x:Name="counterBtn"
            Text="Нажмите на меня!"
            Clicked="OnCounterClicked"
            HorizontalOptions="Центр" />

    </VerticalStackLayout>

</ContentPage>

Соответствующий MainPage.xaml.cs

использование системы;
пространство имен MyMauiApp;

общедоступный частичный класс MainPage: ContentPage
{
    число интервалов = 0;

    публичная главная страница()
    {
        ИнициализироватьКомпонент();
    }

    частная пустота OnCounterClicked (отправитель объекта, EventArgs e)
    {
        считать++;
        counterBtn.Text = $"Вы нажали {count} раз";
    }
}

Описание компонента

Описание кода C#

Эффект результата

После выполнения в центре экрана отобразится приветственное сообщение и кнопка. Каждый раз, когда вы нажимаете кнопку, текст на кнопке обновляется: «Вы нажали X раз».



Примеры и пояснения MauiProgram.cs

Пример: MauiProgram.cs

using Microsoft.Extensions.Logging;

namespace MyMauiApp;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        
        builder
            .UseMauiApp() //Указываем точку входа приложения
            .ConfigureFonts(шрифты =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            });

#if ОТЛАДКА
        builder.Logging.AddDebug();
#endif

        вернуть строитель.Build();
    }
}

Описание функции

Общие расширения

Подвести итог

MauiProgram.csТочно так же, как ASP.NET CoreStartup.csилиProgram.cs, является основным центром настройки для запуска приложений и регистрации служб. При разработке больших приложений MAUI очень часто этот файл расширяют, добавляя DI, ведение журнала, настройки компонентов и т. д.



Развертывание программ .NET C++ в Ubuntu

Предисловие

Развертывание .NET C++ (приложений C++/CLI или C++ на основе .NET) в системах Ubuntu необходимо выполнять отдельно в зависимости от типа проекта. Если вы используете чистый C++/CLI (полагаясь на .NET Framework), вы не сможете выполнить его непосредственно в Linux, и вам необходимо использоватьКроссплатформенная технология для .NET 6/7/8(Например, C++/CLR → C# или использование C++/Native с совместимостью .NET).

Обзор развертывания

В зависимости от характера приложения развертывание можно разделить на три сценария:

  1. Приложение на чистом C++:Скомпилировано с помощью g++ и выполнено непосредственно в Ubuntu.
  2. .NET Managed C++ (C++/CLI):Необходимо перепроектировать для C# или C++/CLI для Core, поддерживаемого .NET 6 и более поздних версий (поддерживается только Windows).
  3. Гибридное приложение:В основном C# и C++ предоставляют модули производительности в виде библиотек динамических функций (.so).

Установка среды

Установите .NET SDK и необходимые инструменты в Ubuntu:

sudo apt update
sudo apt install -y dotnet-sdk-8.0
sudo apt install -y build-essential

(Можно заменить в зависимости от версииdotnet-sdk-8.0дляdotnet-sdk-7.0или другие версии)

Создать проект

Если вы используете .NET C# в качестве основной программы и вызываете библиотеку C++:

# Создаем основное приложение
новая консоль dotnet -n MyApp
cdMyApp

# Создать собственную библиотеку функций
родной mkdir
родной компакт-диск
нано add.cpp

Пример add.cpp:

extern "C" int add(int a, int b) {
    return a + b;
}

Скомпилируйте как библиотеку общих функций:

g++ -fPIC -shared -o libadd.so add.cpp

Вызов библиотек C++ в C# (.NET)

ВProgram.csприсоединяйтесь:

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

Выполнение и тестирование

Вернитесь в корневой каталог проекта и выполните:

dotnet run

Если результат вывода3 + 5 = 8, что указывает на успешное развертывание.

Выпустить версию развертывания

Программы могут быть упакованы в независимые исполняемые файлы развертывания:

dotnet publish -c Release -r linux-x64 --self-contained true

Сгенерированный исполняемый файл находится в папкеbin/Release/net8.0/linux-x64/publish/

Развертывание Docker-контейнера (необязательно)

Приложения можно помещать в контейнеры с помощью Docker для запуска в любой системе Ubuntu:

# Пример файла Docker
ОТ mcr.microsoft.com/dotnet/runtime:8.0
РАБОЧИЙ ДИАР/приложение
КОПИРОВАТЬ ./опубликовать .
КОПИРОВАТЬ ./native/libadd.so /usr/lib/
ТОЧКА ВХОДА [./MyApp"]

Сборка и выполнение контейнеров:

docker build -t myapp .
docker run --rm myapp

На что следует обратить внимание

Подвести итог

Реальным способом развертывания программ .NET C++ в Ubuntu обычно являетсяC# + собственная библиотека C++". Если исходная программа опирается на C++/CLI, ее необходимо перепроектировать для поддержки кросс-платформенной .NET. Наиболее рекомендуемый способ — использовать .NET 8 + собственные модули C++ с Docker для достижения стабильного, переносимого и согласованного процесса развертывания.




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