Visual Studio는 C++, C#, VB.NET, Python, JavaScript 등 다양한 프로그래밍 언어를 지원하는 Microsoft에서 개발한 IDE(통합 개발 환경)입니다. 데스크톱 애플리케이션, 웹 사이트, 클라우드 서비스 및 모바일 애플리케이션 개발에 적합합니다.
Visual Studio는 Windows 운영 체제를 지원하고 Mac용 Visual Studio는 macOS용으로 설계되었습니다.
[*.cs] indent_style = space indent_size = 43. 파일이 저장되면 형식은 자동으로 다음 규칙을 따릅니다.
명령줄 도구를 통해 컴퓨터에 설치된 .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]
사용하려는 버전이 표시되지 않으면 아직 설치되지 않은 것입니다.
dotnet주문하다이 옵션이 표시되지 않으면 Windows 시작 메뉴를 사용하여 "VS용 개발자 명령 프롬프트"를 검색하여 열 수도 있습니다.
Ctrl + `dotnetSDK를 확인하거나 동작시키는 명령어시스템 사용;
수업 프로그램 {
정적 무효 Main() {
문자열 이름 = "세계";
Console.WriteLine($"안녕하세요, {이름} 님!");
}
}
(x, y) => x + yvar q = list.Where(x => x > 10);await Task.Delay(1000);CS8618은 다음을 의미합니다. 생성자의 끝에서Null을 허용하지 않음의 속성은 초기화되지 않았으므로 C# 컴파일러는 초기화될 수 있다고 경고합니다.null。
공개 클래스 제품 {
공개 문자열 이름 { get; 세트; } // 경고 CS8618
}
public class Product {
public string Name { get; set; }
public Product(string name) {
Name = name;
}
}
public class Product {
public string Name { get; set; } = string.Empty;
}
public class Product {
public string? Name { get; set; }
}
→ 적용대상Name합리적으로 허용되는null상황.
required수정자(C# 11 이상에서 지원)공개 클래스 제품 {
공개 필수 문자열 이름 { get; 세트; }
}
// 호출자를 초기화해야 합니다.
var p = 새 제품 { 이름 = "휴대폰" }; // 좋아요
null:사용건설자또는required。null:다음으로 변경string?。string.Empty)。float그리고System::String예를 들면 다음과 같습니다.
System::String^ tmpStr = "Value: " + someFloat;
언제someFloat매우 크거나 작을 경우 과학 표기법(예: 1.23E+05)으로 자동 표시될 수 있습니다.System::String::Format또는ToString소수 자릿수를 지정하고 과학적 표기법을 피하려면 형식 문자열을 사용하세요.네임스페이스 시스템 사용;
부동소수점 값 = 123456.789f;
// 방법 1: 문자열::형식
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);
F2: 고정 소수점 형식, 소수점 이하 2자리F6: 고정 소수점 형식, 소수점 이하 6자리F{숫자}C++/CLI에서는gcnew System::String(stdStr.c_str())~ 할 것이다std::string(대개ANSI / UTF-8인코딩)을 .NET으로 직접System::String,그리고System::String기대는UTF-16。
만약에stdStrASCII가 아닌 문자(예: 중국어)가 포함된 경우 잘못된 문자가 나타납니다.
marshal_as(제안)네임스페이스를 인용해야 합니다.
#include <msclr/marshal_cppstd.h>
네임스페이스 msclr::interop 사용;
std::string stdStr = "중국어 테스트";
System::String^ netStr = marshal_as<System::String^>(stdStr);
✅ 이 방법을 사용하면 UTF-8/ANSI를 .NET 유니코드로 올바르게 자동 변환할 수 있습니다.
---#include <codecvt>
#include <로케일>
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());
---
std::wstringC++ 측에서 원래 와이드 문자열을 사용하는 경우 직접 사용하십시오.
std::wstring wstr = L"중국어 테스트";
System::String^ netStr = gcnew System::String(wstr.c_str());
---
std::string소스는 UTF-8(현대 시스템에서 공통) → 사용marshal_as또는codecvt。gcnew String(stdStr.c_str()), 문자열이 순수 영어가 아닌 경우..NET(C++/CLI, C#, VB.NET 등과 같은 언어)에서는List<T>일반 컬렉션 카테고리입니다.
모든 유형의 데이터(T)를 저장할 수 있으며 동적 크기 조정 기능을 제공합니다.
그것은 속한다System::Collections::Generic네임스페이스.
//네임스페이스 가져오기
네임스페이스 시스템 사용;
네임스페이스 System::Collections::Generic 사용;
정수 메인()
{
// int를 저장할 목록을 선언합니다.
List^ 숫자 = gcnew List();
//초기화 중에 요소를 직접 추가합니다.
List^ names = gcnew List({ "앨리스", "밥", "찰리" });
}
List^ nums = gcnew List();
//새 요소 추가
숫자->추가(10);
숫자->추가(20);
숫자->추가(30);
//지정된 위치에 삽입
숫자->삽입(1, 15); // 인덱스 1에 15를 삽입
//지정된 값 제거
숫자->제거(20);
//지정된 인덱스 제거
숫자->RemoveAt(0);
// 요소 가져오기
int 값 = 숫자[1]; // 인덱스 1의 요소를 가져옵니다.
// 포함되어 있는지 확인
if (숫자->포함(30))
Console::WriteLine("30개 발견");
// 모든 요소 지우기
숫자->지우기();
// for 루프
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(C++/CLI, C#, VB.NET 등과 같은 언어)에서는Dictionary<TKey, TValue>일반 컬렉션입니다.
"키-값 쌍"을 저장하는 데 사용됩니다. 각 키는 고유해야 하며 값은 반복 가능해야 합니다.
그것은 속한다System::Collections::Generic네임스페이스.
//네임스페이스 가져오기
네임스페이스 시스템 사용;
네임스페이스 System::Collections::Generic 사용;
정수 메인()
{
//키를 int로, 값을 문자열로 사용하여 사전을 생성합니다.
Dictionary^ users = gcnew Dictionary();
// 직접 초기화
사용자->추가(1, "앨리스");
사용자->추가(2, "밥");
사용자->추가(3, "찰리");
0을 반환합니다.
}
Dictionary^ age = gcnew Dictionary();
//새 요소 추가
age->Add("톰", 25);
age->Add("제리", 30);
//값 수정(인덱서를 통해)
연령["톰"] = 26;
// 추가 또는 수정(키가 없으면 자동으로 추가됨)
연령["스파이크"] = 40;
// 요소 삭제
age->Remove("제리");
//액세스 요소
if (연령->ContainsKey("톰"))
{
Console::WriteLine("톰의 나이는 {0}입니다.", age["Tom"]);
}
// 값을 얻으려고 시도합니다(예외 방지).
나이;
if (연령->TryGetValue("스파이크", 연령))
{
Console::WriteLine("스파이크의 나이는 {0}입니다", age);
}
Dictionary^ users = gcnew Dictionary();
사용자->추가(1, "앨리스");
사용자->추가(2, "밥");
사용자->추가(3, "찰리");
// KeyValuePair 루프를 사용합니다.
각각에 대해 (사용자의 KeyValuePair<int, String^> 항목)
{
Console::WriteLine("키 = {0}, 값 = {1}", 항목.키, 항목.값);
}
//키만 가져오기
각각에 대해 (사용자->키의 int 키)
{
Console::WriteLine("키: {0}", 키);
}
//값만 가져오기
각각에 대해(사용자->값의 문자열^ 이름)
{
Console::WriteLine("이름: {0}", 이름);
}
| 속성/메서드 | 설명하다 |
|---|---|
Add(key, value) | 새로운 키-값 쌍 세트 추가 |
Remove(key) | 지정된 키와 해당 값을 제거합니다. |
Clear() | 모든 프로젝트 지우기 |
ContainsKey(key) | 지정된 키가 존재하는지 확인 |
ContainsValue(value) | 지정된 값이 존재하는지 확인하십시오. |
TryGetValue(key, out value) | 예외를 발생시키지 않고 안전하게 값을 가져옵니다. |
Count | 현재 사전의 요소 수 |
Keys | 모든 키의 컬렉션을 반환합니다. |
Values | 모든 값의 컬렉션을 반환합니다. |
키 = 1, 값 = Alice 키 = 2, 값 = Bob 키 = 3, 값 = Charlie 톰의 나이는 26세 스파이크의 나이는 40세다.
.NET C++에서는 다음을 사용할 수 있습니다.System::IO네임스페이스는 파일과 디렉터리를 조작하는 기능을 제공합니다.
모든 파일 정보를 읽고 마지막 수정 날짜를 비교하면 디렉터리의 최신 파일을 얻을 수 있습니다.
다음은 지정된 디렉터리에서 최신 파일을 가져오는 방법을 보여주는 전체 예입니다.
#include "stdafx.h"
#include <iostream>
#include <cliext/벡터>
#include <System.IO>
네임스페이스 시스템 사용;
네임스페이스 System::IO 사용;
네임스페이스 클라이언트 사용;
정수 메인()
{
시도하다
{
//디렉터리 경로 지정
String^ 디렉토리 경로 = "C:\\Your\\Directory\\Path";
//디렉토리가 존재하는지 확인
if (!디렉토리::존재(디렉토리경로))
{
Console::WriteLine("디렉토리가 존재하지 않습니다: {0}",directoryPath);
-1을 반환합니다.
}
// 디렉토리의 모든 파일을 가져옵니다.
배열^ 파일 = 디렉토리::GetFiles(디렉토리패스);
//디렉토리에 파일이 없는 경우
if (파일->길이 == 0)
{
Console::WriteLine("디렉토리에 파일이 없습니다.");
0을 반환합니다.
}
//최신 파일 찾기
문자열^ 최신파일 = nullptr;
날짜시간 최신시간 = 날짜시간::MinValue;
각각에 대해 (파일의 String^ 파일)
{
// 파일의 마지막 수정 시간을 가져옵니다.
DateTime lastWriteTime = 파일::GetLastWriteTime(파일);
// 시간을 비교하고 최신 파일 정보를 업데이트합니다.
if (lastWriteTime > 최신 시간)
{
최신 시간 = lastWriteTime;
최신파일 = 파일;
}
}
// 최신 파일 정보 출력
Console::WriteLine("최신 파일: {0}", NewFile);
Console::WriteLine("마지막 수정 시간: {0}", NewTime);
}
잡기 (예외^ 예)
{
Console::WriteLine("오류가 발생했습니다: {0}", ex->Message);
}
0을 반환합니다.
}
Directory::GetFiles지정된 디렉터리의 모든 아카이브 경로를 가져오는 방법입니다.File::GetLastWriteTime각 파일의 마지막 수정 시간을 가져오는 방법입니다.try-catch이 블록은 존재하지 않거나 액세스할 수 없는 디렉터리와 같은 잠재적인 예외를 포착합니다.System.Reflection메타데이터를 검사하고 조작하기 위한 도구를 제공하는 .NET 프레임워크의 네임스페이스로, 개발자가 런타임에 유형, 메서드, 속성 등을 동적으로 검사하고 개체를 동적으로 생성 및 조작할 수 있도록 합니다.
Assembly: 로드된 어셈블리를 나타내며 어셈블리를 로드, 탐색 및 반영하는 방법을 제공합니다.Type: 카테고리, 구조, 인터페이스 등을 포함한 타입을 표현하고, 타입 정보를 얻는 기능을 제공합니다.MethodInfo: 개발자가 메서드 속성을 검사하고 동적으로 호출할 수 있도록 메서드 정보를 나타냅니다.PropertyInfo: 속성 정보를 나타내고 속성 메타데이터에 대한 액세스를 제공합니다.FieldInfo: 필드 정보를 나타내며 해당 유형의 필드에 액세스하는 데 사용할 수 있습니다.다음은 용도입니다System.Reflection유형과 메소드를 동적으로 확인하고 메소드를 호출하는 방법을 보여주는 샘플 프로그램입니다.
//간단한 예제 클래스 정의
공개 클래스 SampleClass {
공개 문자열 SayHello(문자열 이름) {
return $"안녕하세요, {name} 님!";
}
}
// Reflection을 사용하여 메서드를 동적으로 호출합니다.
시스템 사용;
System.Reflection 사용;
수업 프로그램 {
정적 무효 Main() {
//SampleClass 클래스의 인스턴스 생성
유형 샘플 유형 = typeof(SampleClass);
개체 샘플 인스턴스 = Activator.CreateInstance(sampleType);
// SayHello 메소드 정보 얻기
MethodInfo methodInfo = SampleType.GetMethod("SayHello");
// SayHello 메서드를 동적으로 호출합니다.
object result = methodInfo.Invoke(sampleInstance, new object[] { "World" });
Console.WriteLine(결과); // 출력: 안녕하세요, 세계!
}
}
위의 예에서는Activator.CreateInstance카테고리의 인스턴스를 생성하고 사용하려면MethodInfo.Invoke메소드 호출SayHello。
System::Management::ManagementClassWMI(Windows Management Instrumentation) 작업을 위해 제공되는 .NET Framework의 클래스 중 하나입니다. 이를 통해 개발자는 하드웨어, 운영 체제, 네트워크 설정 등과 같은 시스템 정보를 읽고 조작하고 관리할 수 있습니다.
System::Management위치해있습니다System.Management.dll이 함수를 사용하기 전에 네임스페이스에 참조를 추가해야 합니다.
// C++/CLI 작성 방법
네임스페이스 시스템 사용;
네임스페이스 System::Management 사용;
정수 메인() {
{를 시도해보세요
ManagementClass^ mc = gcnew ManagementClass("Win32_OperatingSystem");
각각에 대해 (ManagementObject^ mo in mc->GetInstances()) {
Console::WriteLine("OS 이름: {0}", mo["Caption"]);
}
}
잡기 (예외^ 예) {
Console::WriteLine("오류: {0}", ex->Message);
}
0을 반환합니다.
}
System.Management.dll참조.ManagementException。ManagementObjectManagementObjectSearcherManagementBaseObjectnet stop winmgmt winmgmt /resetrepository net start winmgmt
Get-WmiObject Win32_NetworkAdapterConfiguration방법 2: WMI 테스트 도구(wbemtest)
wbemtestroot\cimv2연결하고SELECT * FROM Win32_NetworkAdapterConfigurationDISM /Online /Cleanup-Image /RestoreHealth수리 시스템 이미지.NET C++(또는 더 일반적으로 C++/CLI) 프로젝트에서 파일의 HTML 테이블을 읽고 구문 분석하려면 일반적으로 외부 HTML 구문 분석 라이브러리를 사용해야 합니다. 표준 .NET 프레임워크와 C++ 자체에는 강력한 HTML DOM 구문 분석 기능이 내장되어 있지 않기 때문입니다. 일반적이고 효율적인 옵션은 C# 또는 다른 .NET 언어로 라이브러리를 사용한 다음 이를 C++/CLI에서 참조하는 것입니다.
C++/CLI 프로젝트의 경우 가장 실용적이고 권장되는 접근 방식은 다음을 사용하는 것입니다.Html Agility Pack(HAP), 매우 인기 있는 .NET HTML 파서입니다. HAP는 C#으로 작성되었지만 C++/CLI를 포함한 모든 .NET 언어에서 참조로 원활하게 사용할 수 있습니다.
HtmlAgilityPack。다음은 로컬 HTML 파일을 읽고 그 안에 있는 첫 번째 테이블을 구문 분석하는 방법을 보여주는 C++/CLI 코드 조각입니다(<table>) 콘텐츠:
#<System.dll>사용
#<System.Xml.dll>사용
#<HtmlAgilityPack.dll>사용 // 참조가 포함되어 있는지 확인
네임스페이스 시스템 사용;
네임스페이스 System::IO 사용;
네임스페이스 HtmlAgilityPack 사용;
무효 ParseHtmlTable(String^ filePath)
{
//파일이 존재하는지 확인
if (!파일::존재(파일 경로))
{
Console::WriteLine("오류: 파일이 존재하지 않습니다.");
반품;
}
// 1. HTML 파일 로드
HtmlDocument^ doc = gcnew HtmlDocument();
시도하다
{
//파일에서 HTML 로드
doc->로드(파일 경로);
}
잡기 (예외^ 예)
{
Console::WriteLine("파일을 로드하는 동안 오류가 발생했습니다: " + ex->Message);
반품;
}
// 2. 첫 번째 <table>을 선택합니다. 노드
HtmlNode^ table = doc->DocumentNode->SelectSingleNode("//table");
if (테이블 != nullptr)
{
Console::WriteLine("HTML 테이블을 찾아 구문 분석을 시작합니다...");
// 3. <tr>를 모두 선택합니다. (테이블 열) 노드
HtmlNodeCollection^ 행 = 테이블->SelectNodes(".//tr");
if (행 != nullptr)
{
각각에 대해(행의 HtmlNode^ 행)
{
// 4. <td> 또는 <일> (셀) 노드
// |를 사용합니다. 연산자를 사용하여 <td> 또는 <일>
HtmlNodeCollection^ 셀 = 행->SelectNodes("td | th");
if (셀 != nullptr)
{
String^ rowData = "";
각각에 대해(셀의 HtmlNode^ 셀)
{
// 셀의 내부 텍스트를 가져오고 앞뒤 공백을 제거합니다.
rowData += 셀->InnerText->Trim() + "\t";
}
콘솔::WriteLine(rowData);
}
}
}
그렇지 않으면
{
Console::WriteLine("<tr> 태그를 테이블에서 찾을 수 없습니다.");
}
}
그렇지 않으면
{
Console::WriteLine("<table> 태그를 파일에서 찾을 수 없습니다.");
}
}
int main(array^ args)
{
// "your_html_file.html"을 실제 파일 경로로 바꾸세요.
String^ htmlFilePath = "C:\\path\\to\\your_html_file.html";
ParseHtmlTable(htmlFilePath);
0을 반환합니다.
}
doc->Load(filePath): 로컬 HTML 파일을 HAP의 DOM 구조로 읽어옵니다.SelectSingleNode("//table"): XPath 표현식을 사용하여 파일의 첫 번째 항목을 선택하세요.<table>요소.SelectNodes(".//tr"): 현재 노드를 선택합니다(<table>) 아래 모두<tr>요소.SelectNodes("td | th"):선택하다<tr>노드 아래의 모든 것<td>또는<th>요소.cell->InnerText: 셀 내용의 일반 텍스트를 가져오면 HTML 태그가 자동으로 제거됩니다.gcnew그리고^:이는 .NET 개체(CLR 가비지 수집 메커니즘으로 관리됨)를 만들고 관리하기 위해 C++/CLI에서 사용하는 구문입니다.HttpListener클래스를 사용하면 C++/CLI 애플리케이션에서 간단한 자체 호스팅 HTTP 서버를 만들 수 있습니다.
Visual Studio(예: CLR 콘솔 애플리케이션)에서 C++/CLI 프로젝트를 만들고 참조되는지 확인하세요.System.dll。
다음이 사용됩니다HttpListener간단한 API 서버를 생성하고 GET 요청을 처리하는 C++/CLI 코드입니다.
#<System.dll>사용
네임스페이스 시스템 사용;
네임스페이스 System::Net 사용;
네임스페이스 System::Threading::Tasks 사용;
네임스페이스 System::Text 사용;
// 들어오는 HTTP 요청을 처리하는 함수
무효 HandleRequest(HttpListenerContext^ 컨텍스트)
{
HttpListenerRequest^ 요청 = 컨텍스트->요청;
HttpListenerResponse^ 응답 = 컨텍스트->응답;
//기본 응답 설정
응답->ContentType = "application/json; charset=utf-8";
String^ responseString = "";
응답->StatusCode = 200; // 기본값은 괜찮습니다.
// 요청한 경로와 메소드를 확인합니다.
String^ url = 요청->RawUrl->ToLower();
if (url == "/api/status" && 요청->HttpMethod == "GET")
{
// GET /api/status 요청 처리
responseString = "{\"상태\": \"실행 중인 서버\", \"언어\": \"C++/CLI\"}";
}
그렇지 않으면
{
// 정의되지 않은 다른 요청을 처리합니다.
응답->StatusCode = 404; // 찾을 수 없음
responseString = "{\"오류\": \"404 찾을 수 없음\", \"경로\": \"" + url + "\"}";
}
// 문자열 응답을 바이트로 변환하고 출력 스트림에 씁니다.
array^ buffer = Encoding::UTF8->GetBytes(responseString);
응답->ContentLength64 = 버퍼->길이;
시도하다
{
응답->OutputStream->쓰기(버퍼, 0, 버퍼->길이);
응답->OutputStream->Close();
}
잡기 (예외^)
{
// 쓰기 오류를 무시합니다. 일반적으로 클라이언트 연결이 끊어집니다.
}
}
// HttpListener를 시작하는 함수
무효 StartListener(String^ 접두사)
{
HttpListener^ 리스너 = gcnew HttpListener();
시도하다
{
리스너->접두사->추가(접두사);
리스너->시작();
Console::WriteLine("C++/CLI API 서버가 시작되었습니다. 수신 중: {0}", 접두사);
}
잡기 (예외^ 예)
{
Console::WriteLine("HttpListener를 시작하는 중 오류가 발생했습니다. 권한이나 주소가 사용 중인지 확인하세요.");
Console::WriteLine("오류 메시지: {0}", ex->Message);
반품;
}
// 비동기 루프, 요청을 계속 수신합니다.
while(리스너->IsListening)
{
시도하다
{
// 동기적으로 다음 요청을 기다립니다.
HttpListenerContext^ 컨텍스트 = 리스너->GetContext();
// 작업을 사용하여 요청을 비동기적으로 처리하여 청취 루프가 차단되는 것을 방지합니다.
작업::Factory->StartNew(gcnew Action<HttpListenerContext^>(&HandleRequest), context);
}
잡기 (예외^)
{
// 리스너가 중지되면 예외가 발생합니다.
휴식;
}
}
if(리스너->IsListening)
{
리스너->닫기();
}
}
int main(array^ args)
{
//모니터링을 위한 URL 접두사 설정
// 참고: Windows에서는 관리자 권한이 필요하거나 netsh를 사용하여 URL 네임스페이스를 등록해야 할 수 있습니다.
String^ listeningPrefix = "http://localhost:8080/";
StartListener(listeningPrefix);
Console::WriteLine("서버를 종료하려면 아무 키나 누르십시오...");
콘솔::ReadKey(true);
0을 반환합니다.
}
Windows 시스템에서 관리자 권한으로 코드가 실행되지 않으면,HttpListener특정 포트를 수신할 때 액세스 거부 예외가 발생할 수 있습니다. 다음 명령을 사용하면 관리자가 아닌 계정이 서버를 실행할 수 있도록 URL 네임스페이스를 등록할 수 있습니다.
netsh http add urlacl url=http://+:8080/ user=Everyone
~에http://+:8080/코드에 사용된 실제 접두사로 바꿔야 합니다.
ASP.NET Core에서 HTTP API 서버를 만들기 위한 표준 프로젝트 유형은 Web API입니다. 최신 .NET 버전(.NET 6 이상) 권장Minimal APIs빠르고 가볍게 API 엔드포인트를 생성합니다.
최소 API는 최소한의 파일과 코드 줄을 사용하여 시작 논리(Startup)와 라우팅 정의(Routing)를 단일 파일로 결합합니다.Program.cs가운데.
새로운 최소 API 프로젝트를 생성하려면 .NET CLI(명령줄 인터페이스)를 사용할 수 있습니다.
dotnet new web -n MinimalApiServer
cd MinimalApiServer
다음은 최소 API 서버 코어 파일입니다.Program.cs전체 콘텐츠. 이는 서버 시작, 미들웨어 및 두 개의 API 엔드포인트(GET 하나, POST 하나)를 정의합니다.
// 1. WebApplication 인스턴스 생성(기존 Startup 카테고리 대체)
var builder = WebApplication.CreateBuilder(args);
// 2. 서비스 등록(서비스 구성)
//여기서 데이터베이스 연결, 확인 서비스 등을 추가할 수 있습니다.
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(); // Swagger/OpenAPI 지원 활성화
var app = builder.Build();
// 3. HTTP 요청 파이프라인 구성(미들웨어 구성)
// 개발 환경에서 테스트를 위해 Swagger UI를 활성화합니다.
if(app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
// HTTPS 리디렉션 활성화(프로덕션 환경에 권장)
// app.UseHttpsRedirection();
// 4. API 엔드포인트 정의(Endpoint Definition)
// POST 요청에 사용되는 엔터티 클래스
공개 기록 제품(int Id, 문자열 이름, 십진수 가격);
// 요청 엔드포인트 가져오기: /api/hello
app.MapGet("/api/hello", () =>
{
return Results.Ok(new { message = "ASP.NET Core Minimal API에서 안녕하세요!", timestamp = DateTime.Now });
});
// 요청 엔드포인트 가져오기: /api/products/{id}
app.MapGet("/api/products/{id}", (int id) =>
{
// 데이터베이스에서 제품을 쿼리한다고 가정합니다.
if (id == 1)
{
return Results.Ok(새 제품(1, "노트북", 1200.00m));
}
return Results.NotFound(new { error = $"제품 ID {id}을(를) 찾을 수 없음" });
});
// POST 요청 엔드포인트: /api/products
app.MapPost("/api/products", (제품 제품) =>
{
// ASP.NET Core는 자동으로 JSON 요청 본문을 Product 엔터티로 역직렬화합니다.
Console.WriteLine($"새 제품을 받았습니다: {product.Name} (ID: {product.Id})");
//실제 응용에서는 여기서 데이터베이스 추가 작업을 수행하게 됩니다.
//Return 201 생성된 상태 코드
return Results.Created($"/api/products/{product.Id}", product);
});
// 5. 애플리케이션을 시작하고 HTTP 요청 수신을 시작합니다.
app.Run();
dotnet run。http://localhost:5000또는https://localhost:7000달리다./api/hello (GET)。/swagger정의된 모든 API 엔드포인트를 테스트하기 위한 Swagger UI를 보기 위한 경로입니다.app.UseSwagger()、app.UseHttpsRedirection()등, 이러한 구성 요소는 요청 처리 파이프라인을 순서대로 구성합니다. 모든 요청은 이러한 미들웨어를 통해 흐릅니다.app.MapPost예제에서 클라이언트가 JSON 데이터를 보내면 ASP.NET Core는 자동으로 이를 C#으로 변환(역직렬화)합니다.Product기록.대부분의 최신 Chromebook은 Crostini 프로젝트를 통해 Linux 애플리케이션 실행을 지원합니다.
설정 > 고급 > 개발자 > Linux 활성화(베타)。sudo apt update
sudo apt install -y dotnet-sdk-7.0
클라우드 플랫폼을 사용하면 로컬에 소프트웨어를 설치하지 않고도 Chromebook에서 .NET 프로그램을 실행할 수 있습니다.
Chromebook은 컨테이너를 사용하여 애플리케이션을 실행할 수 있도록 지원하며 Docker를 통해 .NET 환경을 시작할 수 있습니다.
docker pull mcr.microsoft.com/dotnet/runtime:7.0
.NET 6부터 애플리케이션은 Linux를 포함한 여러 플랫폼에서 실행될 수 있습니다. 앱을 크로스 플랫폼 형식으로 컴파일하고 Chromebook에 배포하세요.
dotnet publish -c Release -r linux-x64 --self-contained
시스템 사용;
System.Windows.Forms 사용;
공개 클래스 MyForm : 양식 {
공개 MyForm() {
버튼 btn = new Button();
btn.Text = "클릭하세요";
btn.Click += (s, e) => MessageBox.Show("버튼을 클릭하셨습니다!");
컨트롤.추가(btn);
}
[STAT스레드]
정적 무효 Main() {
애플리케이션.EnableVisualStyles();
Application.Run(new MyForm());
}
}
// MainWindow.xaml
<창 x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Title="WPF 예" Height="200" Width="300">
<스택패널>
<Button Content="나를 클릭하세요" Click="Button_Click"/>
</StackPanel>
</창>
// MainWindow.xaml.cs
private void Button_Click(객체 전송자, RoutedEventArgs e) {
MessageBox.Show("버튼을 클릭하셨습니다!");
}
// 메인페이지.xaml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
x:Class="MyApp.MainPage">
<VerticalStackLayout Spacing="25" Padding="30">
<Button Text="나를 클릭하세요" Clicked="OnButtonClicked"/>
</VerticalStackLayout>
</콘텐츠페이지>
// 메인페이지.xaml.cs
private void OnButtonClicked(객체 전송자, EventArgs e) {
wait DisplayAlert("알림", "버튼을 클릭하셨습니다!", "확인");
}
| 기술 | 사용 | 플랫폼 |
|---|---|---|
| WinForms | 데스크탑 애플리케이션을 신속하게 개발 | Windows |
| WPF | 복잡한 데스크탑 애플리케이션, MVVM 패턴 | Windows |
| MAUI | 크로스 플랫폼 애플리케이션 | Windows、macOS、Android、iOS |
| Blazor | 웹 프론트엔드 개발 | 크로스 플랫폼(웹) |
다음 예에서는 사용 방법을 보여줍니다.Controls->GetEnumerator().NET 양식의 모든 하위 컨트롤을 하나씩 반복하고 해당 속성을 일괄적으로 수정하는 방법입니다.
시스템 사용;
System.드로잉 사용;
System.Windows.Forms 사용;
공개 클래스 FormExample : 양식
{
공개 양식예제()
{
//일부 컨트롤 초기화
버튼 버튼1 = 새 버튼 { 텍스트 = "버튼 1", 위치 = 새 포인트(10, 10) };
TextBox textBox1 = new TextBox { 위치 = new Point(10, 50) };
컨트롤.추가(버튼1);
Controls.Add(textBox1);
// GetEnumerator()를 사용하여 컨트롤 속성을 탐색하고 수정합니다.
수정컨트롤();
}
개인 무효 ModifyControls()
{
var 열거자 = Controls.GetEnumerator();
동안(열거자.MoveNext())
{
제어 제어 = (Control)enumerator.Current;
// 설정예 : 모든 컨트롤의 배경색을 연한 파란색으로 설정
control.BackColor = Color.LightBlue;
// 컨트롤 항목이 TextBox인 경우 편집할 수 없도록 만듭니다.
if (컨트롤은 TextBox입니다)
{
제어.활성화 = 거짓;
}
}
}
//애플리케이션 시작
공개 정적 무효 Main()
{
Application.Run(새 FormExample());
}
}
**GetEnumerator()**: 'Controls.GetEnumerator()' 메서드를 사용하여 컨트롤 항목의 열거자를 가져오면 모든 하위 컨트롤을 탐색할 수 있습니다.
**조건부 수정**: `while` 루프에서 배경색을 연한 파란색으로 설정하는 등 각 `Control` 개체의 속성을 수정하고 `TextBox`를 편집 불가능으로 설정하는 등 컨트롤 유형에 따라 특정 수정을 수행합니다.
**사용**: 이 방법은 UI 스타일을 조정하거나 특정 상황에서 여러 컨트롤을 비활성화하는 등 일괄적으로 속성을 수정해야 할 때 매우 효과적입니다.
실행 후 모든 컨트롤의 배경색은 연한 파란색으로 변경되고 모든 'TextBox' 컨트롤은 편집 불가능으로 설정됩니다.
C++/CLI에서는MessageBox::Show대화 상자를 팝업하는 데 사용되는 정적 메서드입니다. 가장 완벽한 통화 방법에는 메시지 내용, 제목, 버튼 유형, 아이콘 및 기본 버튼이 포함됩니다.
네임스페이스 System::Windows::Forms 사용;
//가장 간단한 디스플레이
MessageBox::Show("작업 완료");
//전체 매개변수 버전
DialogResult 결과 = MessageBox::Show(
"이 파일을 삭제하시겠습니까?", // 메시지 내용(텍스트)
"삭제 확인", // 창 제목(캡션)
MessageBoxButtons::YesNo, // 버튼 조합
MessageBoxIcon::Warning, // 프롬프트 아이콘
MessageBoxDefaultButton::Button2 // 기본 포커스는 두 번째 버튼에 있습니다(아니요).
);
버튼에 예/아니요, 확인/취소 등 여러 옵션이 포함된 경우 확인해야 합니다.DialogResult후속 논리를 결정합니다.
if (결과 == DialogResult::예) {
//삭제 로직 실행
} 그렇지 않으면 {
//작업 취소
}
| 범주 | 공통 옵션 | 설명하다 |
|---|---|---|
| MessageBoxButtons | OK, OKCancel, YesNo, YesNoCancel | 대화 상자 아래에 표시되는 버튼을 정의합니다. |
| MessageBoxIcon | Information, Warning, Error, Question | 왼쪽에 표시되는 아이콘과 시스템 사운드 효과를 정의합니다. |
| DialogResult | OK, Cancel, Yes, No | Show메서드의 반환 값은 사용자가 누른 키를 나타냅니다. |
왜냐하면MessageBox::Show받은 것은 .NET입니다.System::String^, 네이티브 C++ 데이터가 있는 경우 이를 변환해야 합니다.
#include <string>
#include <msclr/marshal_cppstd.h>
// MessageBox에 std::string 표시
std::string cpp_str = "핵심 오류";
MessageBox::Show(msclr::interop::marshal_as<System::String^>(cpp_str));
//플로트 형식을 지정하고 표시합니다.
부동 소수점 = 1.23456f;
MessageBox::Show("거리: " + dist.ToString("F2")); // "거리: 1.23" 표시
MessageBoxButtons::OK。using namespace System;그리고using namespace System::Windows::Forms;, 아마도 다른 사람과MessageBox정의가 충돌하므로 전체 이름을 사용하는 것이 좋습니다.System::Windows::Forms::MessageBox::Show(...)。System::Windows::Forms::MessageBox"알림" 또는 "경고"를 표시하고 표준 버튼을 통해 사용자 결정을 얻도록 설계되었습니다. 컨트롤 항목을 동적으로 삽입하는 기능은 없습니다. 텍스트를 입력해야 하는 경우 표준 접근 방식은 상속을 받는 것입니다.System::Windows::Forms::Form사용자 정의 대화 상자를 만듭니다.
이는 레이블, 텍스트 상자 및 확인 버튼이 포함된 간단하고 재사용 가능한 범주입니다.
네임스페이스 시스템 사용;
네임스페이스 System::Windows::Forms 사용;
네임스페이스 System::드로잉 사용;
공개 참조 클래스 CustomInputBox : 공개 양식 {
공개:
TextBox^ txtInput;
버튼^ btnOK;
라벨^ lblPrompt;
CustomInputBox(String^ 제목, String^ 프롬프트) {
//폼의 기본 속성을 설정합니다.
this->텍스트 = 제목;
this->Size = 시스템::드로잉::Size(300, 150);
this->FormBorderStyle = System::Windows::Forms::FormBorderStyle::FixedDialog;
this->StartPosition = FormStartPosition::CenterParent;
this->MaximizeBox = false;
this->MinimizeBox = false;
// 프롬프트 텍스트
lblPrompt = gcnew Label();
lblPrompt->Text = 프롬프트;
lblPrompt->Location = Point(15, 15);
lblPrompt->Size = 시스템::도면::Size(250, 20);
// 입력 상자
txtInput = gcnew TextBox();
txtInput->위치 = Point(15, 40);
txtInput->Size = 시스템::드로잉::Size(250, 20);
// 확인 버튼
btnOK = gcnew 버튼();
btnOK->Text = "확인";
btnOK->DialogResult = System::Windows::Forms::DialogResult::OK;
btnOK->위치 = Point(190, 75);
// 컨트롤 추가
this->컨트롤->추가(lblPrompt);
this->컨트롤->추가(txtInput);
this->컨트롤->추가(btnOK);
//기본 버튼 설정(Enter를 누르는 것은 확인을 클릭하는 것과 같습니다)
this->AcceptButton = btnOK;
}
};
사용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);
}
}
}
| 계획 | 구현 복잡성 | 이점 | 결점 |
|---|---|---|---|
| 사용자 정의 양식 | 가운데 | 레이아웃에 대한 전체 제어 및 유효성 검사 논리(예: 제한된 숫자)를 추가하는 기능입니다. | 더 많은 인터페이스 코드를 작성해야 합니다. |
| VB Interaction | 낮은 | 코드 한 줄이 완성되었습니다. | 인용이 필요하다Microsoft.VisualBasic, 스타일은 고정되어 있으며 조정할 수 없습니다. |
| Win32 API | 높은 | .NET 환경이 필요하지 않습니다. | C++/CLI로 개발하는 것은 매우 간단하고 유지 관리가 어렵습니다. |
delete ibox;또는 범위가 끝난 후 리소스를 올바르게 해제하는지 확인하세요.ShowDialog()오히려Show(), 이는 사용자가 기본 창을 작동하기 전에 입력 창을 닫아야 하며 직접 얻을 수 있도록 보장합니다.DialogResult。PictureBox에 빨간색 십자가가 표시됩니다.OnPaint실행 중 예외가 발생했습니다.
그러나 .NET에서는 기본적으로 이 예외를 무시하고 일반 로그나 중단 지점에 표시하지 않으므로 디버깅이 어렵습니다.
| 이유 | 설명하다 |
|---|---|
| 비트맵이 삭제되었습니다. | PictureBox는 여전히 비트맵에 대한 참조를 보유하고 있으며 릴리스된 리소스는 그림판 중에 액세스됩니다. |
| 소스 스트림이 닫혔습니다. | Stream을 사용하여 Bitmap을 만든 다음 Stream을 닫으면 Bitmap 데이터가 유효하지 않게 됩니다. |
| 크로스 스레드 액세스 | 백그라운드 스레드는 UI 스레드와 충돌하는 Bitmap을 직접 수정하거나 할당합니다. |
| GDI+ 리소스 고갈 | 많은 수의 비트맵이 삭제되지 않아 GDI+ 메모리가 부족합니다. |
| 예외 메시지 | 근본 원인 | 솔루션 방향 |
|---|---|---|
| ArgumentException: Parameter is not valid | 비트맵이 삭제되었거나 스트림이 닫혔습니다. | 딥 카피 비트맵, 스트림에 의존하지 않음 |
| InvalidOperationException: object is in use elsewhere | 여러 스레드에서 동시에 동일한 Bitmap에 액세스 | 잠금을 추가하거나 UI 스레드에 다시 호출 |
| OutOfMemoryException | GDI+ 리소스가 소진되었거나 이미지 형식이 지원되지 않습니다. | 기존 Bitmap은 즉시 폐기하고 이미지 소스를 확인하세요. |
| ExternalException: A generic error occurred in GDI+ | 비트맵에 해당하는 파일 또는 스트림이 더 이상 존재하지 않습니다. | 비트맵 수명 동안 소스가 여전히 유효한지 확인하세요. |
적십자 문제가 자주 발생하지 않을 수 있으므로 다음 순서대로 진행하는 것이 좋습니다.
OnPaint,
try/catch를 사용하여 예외를 포착하고 다음으로 출력합니다.Debug.WriteLine,
캡처 후 실행this.Image = null지속적인 적십자 표시를 피하십시오.Environment.StackTrace, 누가 언제 리소스를 공개했는지 알아보세요.Imagenull로 설정한 다음 Dispose를 수행합니다.모든 이미지 변경 작업은 스레드 안전과 올바른 처리 순서를 보장하기 위해 이 모드를 통해 수행됩니다.
// C++/CLI
void SafeUpdateImage(Bitmap^ newBmp) {
if (pictureBox1->InvokeRequired) {
pictureBox1->Invoke(gcnew Action<비트맵^>(
this, &YourClass::SafeUpdateImage), newBmp);
반품;
}
Bitmap^ old = safe_cast<Bitmap^>(pictureBox1->Image);
pictureBox1->이미지 = newBmp; // 먼저 새 그림으로 변경
if (old != nullptr) old->Dispose(); // 기존 사진을 다시 폐기합니다.
}
// 오류: 스트림이 닫힌 후 비트맵이 유효하지 않습니다.
(var stream = new FileStream(path, FileMode.Open))을 사용하여 {
pictureBox1.Image = 새로운 비트맵(스트림);
}
// 정확함: 딥 카피, 스트림에 의존하지 않음
비트맵이 로드되었습니다.
(var stream = new FileStream(path, FileMode.Open)) 사용
(var tmp = new Bitmap(stream))을 사용하여 {
로드됨 = 새 비트맵(tmp);
}
pictureBox1.Image = 로드됨;
| 확인항목 | 확인방법 |
|---|---|
| 비트맵이 다른 곳에 배치되는지 여부 | SafeUpdateImage를 통해 통합적으로 이미지 변경 및 StackTrace 기록 |
| 소스 스트림이 닫혀 있는지 여부 | 대신 딥 카피를 사용하세요new Bitmap(tmp) |
| 스레드 간 액세스 여부 | 이미지 변경 시 InvokeRequired를 확인하고, 항상 UI 실행 스레드로 다시 호출합니다. |
| 적십자가 즉시 나타나는가, 아니면 잠시 후에 나타나는가? | 바로 나타난다면 소스 문제일 수 있습니다. 잠시 후에는 Dispose 타이밍 문제일 수 있습니다. |
스택 추적에 다음 오류 메시지가 나타나면 개발자는 확인해야 할 수 있습니다.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개체의 내용입니다.
양식이나 컨트롤의 소스 코드에 접근할 수 있는 경우 덮어쓰기를 권장합니다.WndProc방법으로 직접 기록하거나 확인Message m콘텐츠.
보호된 재정의 void WndProc(참조 메시지 m)
{
시도하다
{
//메시지 내용을 기록합니다.
Console.WriteLine($"메시지 세부 정보: hWnd={m.HWnd}, Msg={m.Msg}, WParam={m.WParam}, LParam={m.LParam}");
base.WndProc(ref m);
}
잡기 (예외예외)
{
Console.WriteLine($"예외: {ex}");
던지다; // 원래 예외 동작을 유지합니다.
}
}
이 코드는 메시지가 수신될 때마다 관련 정보를 기록하고 개발자가 이를 추가로 분석할 수 있도록 합니다.
에서 오류가 발생하는 경우Dispose메소드 내에서 예외 처리를 추가하여 관련 정보를 확인할 수 있습니다.
보호된 재정의 void Dispose(bool disposing)
{
시도하다
{
만약 (처분)
{
// 리소스 해제
}
base.Dispose(처리);
}
잡기 (예외예외)
{
Console.WriteLine($"Dispose 예외: {ex}");
던지다;
}
}
이렇게 하면 리소스를 해제할 때 중요한 오류 정보가 무시되지 않습니다.
오류가 발생한 위치를 확인할 수 없는 경우 전역 예외 처리를 사용하여 스택 추적 및 관련 정보를 기록할 수 있습니다.
AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
{
Exception ex = (Exception)args.ExceptionObject;
Console.WriteLine($"Unhandled Exception: {ex}");
Environment.Exit(1);
};
Visual Studio를 사용하여 중단점을 설정할 수 있습니다.WndProc또는Dispose방법과 확인Message개체의 내용입니다.
HWnd: 메시지를 수신하는 창의 핸들입니다.Msg: 메시지의 ID입니다.WParam: 추가 메시지 정보(단어 매개변수)입니다.LParam: 추가 메시지 정보(긴 매개변수)입니다.WndProc또는 기존 동작에 영향을 주지 않도록 코드를 수정할 때 주의하세요.Message좌표나 ID가 고정되어 있지 않으므로 조건부 판단을 추가하여 필요한 정보를 필터링하는 것을 고려할 수 있습니다..NET C++/CLI에서는WmClose(Message& m)예System.Windows.Forms.Form내부 보호 방법은 직접 덮어쓸 수 없습니다. 그러나 재정의는 가능합니다.WndProc가로채서 처리하는 방법WM_CLOSE비슷한 재정의를 달성하라는 메시지WmClose효과.
#include <Windows.h>
#include <System.Windows.Forms.h>
네임스페이스 시스템 사용;
네임스페이스 System::Windows::Forms 사용;
공개 참조 클래스 CustomForm : 공개 양식
{
보호됨:
// WmClose 동작 재정의 시뮬레이션
무효 WmClose(메시지%m)
{
//여기에 사용자 정의 WM_CLOSE 동작을 추가합니다.
if (MessageBox::Show("창을 닫으시겠습니까?", "확인", MessageBoxButtons::YesNo) == DialogResult::Yes)
{
// 정상적인 종료를 위해 기본 동작을 계속 호출합니다.
this->Form::WndProc(m);
}
그렇지 않으면
{
//창이 닫히지 않도록 방지
반환;
}
}
// WndProc를 재정의하고 WM_CLOSE 메시지를 가로채서 WmClose를 호출합니다.
가상 무효 WndProc(Message% m) 재정의
{
const int WM_CLOSE = 0x0010;
if (m.Msg == WM_CLOSE)
{
WmClose(m); // 사용자 정의 WmClose 메소드 호출
}
그렇지 않으면
{
// 다른 메시지 처리
형식::WndProc(m);
}
}
};
[STAT스레드]
int main(array^ args)
{
응용프로그램::EnableVisualStyles();
응용프로그램::SetCompatibleTextRenderingDefault(false);
CustomForm^ form = gcnew CustomForm();
form->Text = "WmClose 동작 예 재정의";
응용 프로그램::실행(양식);
0을 반환합니다.
}
덮어쓰기 시뮬레이션WmClose여기에서 창 닫기 동작을 사용자 정의하는 방법입니다. 확인 대화 상자를 사용하여 사용자에게 창을 닫을 것인지 묻습니다.
덮어쓰기WndProc가로채는 방법WM_CLOSE메시지를 전송하고 이를 사용자 정의에 위임합니다.WmClose방법.
방해받지 않고WM_CLOSE기본 클래스를 호출하는 경우WndProc다른 메시지를 처리하는 방법입니다.
WmClose이 메서드는 덮어쓰기만 시뮬레이션하지만 실제로는 메시지를 가로채서 처리합니다.Form::WndProc기본 동작이 제대로 작동하는지 확인합니다.InvokeRequired예System::Windows::Forms::Control이 속성은 현재 실행 스레드가 컨트롤 항목이 속한 UI 실행 스레드인지 여부를 확인하는 데 사용됩니다. 백그라운드 스레드에서 UI 컨트롤에 액세스할 때 다음을 사용해야 합니다.Invoke작업을 다시 UI 스레드로 마샬링합니다.네임스페이스 시스템 사용;
네임스페이스 System::Windows::Forms 사용;
네임스페이스 System::드로잉 사용;
참조 클래스 ImageHelper {
공개:
정적 무효 SetImageSafe(PictureBox^ pPictureBox, Bitmap^ b) {
if (pPictureBox == nullptr)
반환;
if (pPictureBox->InvokeRequired) {
// MethodInvoker를 사용하여 동일한 함수를 호출하지만 UI 스레드에서 실행합니다.
pPictureBox->호출(
gcnew MethodInvoker(gcnew 액션
InvokeRequired상속에만 사용할 수 있습니다.Control개체(예: PictureBox).true, 이는 현재 실행 스레드가 UI 실행 스레드가 아니므로 사용해야 함을 의미합니다.Invoke。Tuple여러 매개변수를 캡슐화하고 처리를 위해 정적 프록시 함수에 전달합니다.try-catch예외를 포착하면 프로그램 충돌이 방지됩니다.
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
네임스페이스 시스템 사용;
네임스페이스 System::Windows::Forms 사용;
네임스페이스 System::드로잉 사용;
void SafeAssignImage(PictureBox^ pPictureBox, Bitmap^ b) {
if (pPictureBox->InvokeRequired) {
pPictureBox->Invoke(gcnew MethodInvoker(gcnew 대리자 {
{를 시도해보세요
if (b != nullptr) {
if (pPictureBox->이미지 != nullptr)
pPictureBox->이미지 삭제;
pPictureBox->이미지 = b;
}
} catch (System::Exception^ ex) {
Console::WriteLine("이미지 설정 실패: " + ex->Message);
}
}));
} 그렇지 않으면 {
{를 시도해보세요
if (b != nullptr) {
if (pPictureBox->이미지 != nullptr)
pPictureBox->이미지 삭제;
pPictureBox->이미지 = b;
}
} catch (System::Exception^ ex) {
Console::WriteLine("이미지 설정 실패: " + ex->Message);
}
}
}
gcnew MethodInvoker(...)법적 위임이어야 하며 C++11 람다(예: [=]()) 구문은 지원되지 않습니다.BeginInvoke성냥Object^배열 및 구문 분석.Blazor는 개발자가 C# 및 Razor 구문을 사용하여 대화형 웹 애플리케이션을 만들 수 있도록 Microsoft에서 출시한 프런트 엔드 프레임워크입니다. JavaScript를 사용하지 않고도 C# 코드를 브라우저에서 실행할 수 있습니다.
Blazor는 ASP.NET Core를 원활하게 통합하고 기존 .NET 애플리케이션과 결합할 수 있으며 종속성 주입, 라우팅, 구성 요소 아키텍처 및 기타 기능을 지원합니다.
Blazor는 .NET을 주요 기술로 사용하는 개발자를 위해 Node.js의 통합 JavaScript 언어에 해당하는 솔루션을 제공하여 프런트엔드와 백엔드 모두에서 C#을 사용할 수 있도록 하여 보다 일관된 개발 경험을 제공합니다.
.NET MAUI(Multi-platform App UI)는 Microsoft에서 출시한 크로스 플랫폼 애플리케이션 프레임워크입니다. C# 및 XAML을 사용하여 코드를 한 번 작성하고 이를 Windows, Android, iOS 및 macOS에 배포할 수 있습니다.
.NET MAUI의 Blazor 하이브리드 모드를 사용하면 기본 애플리케이션에 웹 UI를 포함할 수 있으므로 개발자는 Razor 구성 요소를 사용하여 기본 API에 계속 액세스하면서 크로스 플랫폼 사용자 인터페이스를 개발할 수 있습니다.
.NET MAUI는 현재 가장 완벽한 .NET 크로스 플랫폼 솔루션입니다. Blazor와 결합하여 웹, 데스크톱 및 모바일 플랫폼을 위한 통합 애플리케이션 개발을 달성하고 .NET 개발자에게 언어와 기술이 일관되는 완전한 경험을 제공할 수 있습니다.
.NET MAUI는 API 호출을 통해 ASP.NET Core에서 생성된 백엔드 서비스와 통합되어 완전한 "프런트 엔드 앱 + 백엔드 API" 아키텍처를 형성할 수 있습니다.
ASP.NET Core는 "백엔드 서비스" 및 "웹 애플리케이션"의 문제를 해결하는 반면, .NET MAUI는 "클라이언트 측 크로스 플랫폼 앱"에 중점을 둡니다. 이 둘은 상호 배타적이지 않지만 완전한 솔루션을 형성하기 위해 함께 사용되는 경우가 많습니다.
다음은 클릭할 때 텍스트 수를 업데이트하는 버튼을 화면에 표시하는 .NET MAUI를 사용하여 구축된 크로스 플랫폼 애플리케이션의 예입니다.
<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">
<라벨 x:Name="counterLabel"
Text="아직 버튼을 클릭하지 않았습니다."
글꼴 크기="24"
수평 옵션="가운데" />
<버튼 텍스트="나를 클릭하세요"
Clicked="OnCounterClicked"
수평 옵션="가운데" />
</VerticalStackLayout>
</콘텐츠페이지>
네임스페이스MauiApp;
공개 부분 클래스 MainPage : ContentPage
{
정수 개수 = 0;
공개 메인페이지()
{
초기화구성요소();
}
개인 무효 OnCounterClicked(객체 보낸 사람, EventArgs e)
{
카운트++;
counterLabel.Text = $"{count}번 클릭하셨습니다";
}
}
이 간단한 예는 .NET MAUI의 크로스 플랫폼 기능을 보여줍니다. 개발자는 XAML 및 C# 코드를 한 번만 작성하면 여러 플랫폼에서 동시에 실행할 수 있습니다.
MainPage.xaml: UI 인터페이스 정의(XAML 사용)MainPage.xaml.cs: 백엔드 코드(C#)가 이벤트를 처리합니다.MauiProgram.cs: 애플리케이션 시작 및 서비스 등록 지점Platforms폴더: 각 플랫폼(Android, iOS, Windows, MacCatalyst)에 대한 기본 설정Visual Studio를 사용하여 .NET MAUI 프로젝트를 생성하려면 몇 단계만 거치면 되며, 하나의 코드 세트를 동시에 여러 플랫폼에 배포할 수 있으므로 최신 C# 풀엔드 개발자에게 이상적인 선택입니다.
<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="가운데">
<라벨
Text=".NET MAUI에 오신 것을 환영합니다!"
SemanticProperties.HeadingLevel="레벨1"
글꼴 크기="32"
수평 옵션="가운데" />
<버튼
x:이름="counterBtn"
Text="나를 클릭하세요!"
Clicked="OnCounterClicked"
수평 옵션="가운데" />
</VerticalStackLayout>
</콘텐츠페이지>
시스템 사용;
네임스페이스 MyMauiApp;
공개 부분 클래스 MainPage : ContentPage
{
정수 개수 = 0;
공개 메인페이지()
{
초기화구성요소();
}
개인 무효 OnCounterClicked(객체 보낸 사람, EventArgs e)
{
카운트++;
counterBtn.Text = $"{count}번 클릭하셨습니다";
}
}
Spacing(간격) 및Padding(내부 거리).x:NameC# 백엔드에서 액세스하려면Clicked속성은 클릭 이벤트 처리 방법을 지정합니다.InitializeComponent(): XAML에 정의된 UI 구조를 초기화합니다.OnCounterClicked: 클릭 이벤트 처리 방법, 버튼을 클릭할 때마다 버튼 텍스트를 업데이트합니다.counterBtn.Text: XAML에 지정된 이름을 통해 버튼의 속성을 제어합니다.실행이 완료되면 화면 중앙에 환영 메시지와 버튼이 표시됩니다. 버튼을 클릭할 때마다 버튼의 텍스트가 "X번 클릭했습니다."로 업데이트됩니다.
using Microsoft.Extensions.Logging;
namespace MyMauiApp;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp() //애플리케이션의 진입점을 지정합니다.
.ConfigureFonts(글꼴 =>
{
글꼴.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
글꼴.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
#if 디버그
builder.Logging.AddDebug();
#endif
return builder.Build();
}
}
MauiApp예.App.xaml.cs앱 카테고리) 이는 애플리케이션의 루트 구성요소가 됩니다.builder.Services.AddSingleton<MainPage>();이를 통해 예를 들어 App 클래스에서 종속성 주입을 사용하여 MainPage를 로드할 수 있습니다.
serviceProvider.GetService<MainPage>()。
builder.Services.AddTransient<MyViewModel>();
builder.Services.AddSingleton<IMyService, MyService>();
MauiProgram.csASP.NET Core와 마찬가지로Startup.cs또는Program.cs, 애플리케이션 시작 및 서비스 등록을 위한 주요 설정 센터입니다. 대규모 MAUI 애플리케이션을 개발할 때 이 파일을 확장하여 DI, 로깅, 구성 요소 설정 등을 추가하는 것이 매우 일반적입니다.
Ubuntu 시스템에 .NET C++(C++/CLI 또는 .NET 기반 C++ 애플리케이션) 배포는 프로젝트 유형에 따라 별도로 처리해야 합니다. 순수 C++/CLI(.NET Framework 기반)를 사용하는 경우 Linux에서 직접 실행할 수 없으며 다음을 사용해야 합니다..NET 6/7/8용 크로스 플랫폼 기술(예: C++/CLR → C# 또는 .NET 상호 운용성을 갖춘 C++/Native 사용)
애플리케이션의 특성에 따라 배포는 세 가지 시나리오로 나눌 수 있습니다.
Ubuntu에 .NET SDK 및 필요한 도구를 설치합니다.
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 네이티브
CD 네이티브
나노 추가.cpp
add.cpp 예:
extern "C" int add(int a, int b) {
return a + b;
}
공유 함수 라이브러리로 컴파일:
g++ -fPIC -shared -o libadd.so add.cpp
~에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를 사용하여 애플리케이션을 컨테이너화하여 모든 Ubuntu 시스템에서 실행할 수 있습니다.
# 도커파일 예시
mcr.microsoft.com/dotnet/runtime:8.0에서
WORKDIR/앱
복사 ./publish .
복사 ./native/libadd.so /usr/lib/
진입점 ["./MyApp"]
컨테이너 빌드 및 실행:
docker build -t myapp .
docker run --rm myapp
.soWindows에서 사용되는 함수 라이브러리.dll。LD_LIBRARY_PATH。.NET C++ 프로그램을 Ubuntu에 배포하는 실행 가능한 방법은 일반적으로 다음과 같습니다.C# + C++ 네이티브 라이브러리"구조입니다. 원래 프로그램이 C++/CLI에 의존하는 경우 크로스 플랫폼 .NET을 지원하도록 다시 설계해야 합니다. 가장 권장되는 방법은 Docker와 함께 .NET 8 + 네이티브 C++ 모듈을 사용하여 안정적이고 이식 가능하며 일관된 배포 프로세스를 달성하는 것입니다.
email: [email protected]