마이크로소프트에서 개발



Visual Studio

개발 환경 소개

Visual Studio는 C++, C#, VB.NET, Python, JavaScript 등 다양한 프로그래밍 언어를 지원하는 Microsoft에서 개발한 IDE(통합 개발 환경)입니다. 데스크톱 애플리케이션, 웹 사이트, 클라우드 서비스 및 모바일 애플리케이션 개발에 적합합니다.

주요 기능

버전 차이

플랫폼 지원

Visual Studio는 Windows 운영 체제를 지원하고 Mac용 Visual Studio는 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. 비주얼 스튜디오 열기
  2. 메뉴 열 클릭:도구 > 명령줄 > 개발자 명령 프롬프트 문자
  3. 직접 사용할 수 있는 개발 환경 경로가 설정된 명령 프롬프트 창이 열립니다.dotnet주문하다

이 옵션이 표시되지 않으면 Windows 시작 메뉴를 사용하여 "VS용 개발자 명령 프롬프트"를 검색하여 열 수도 있습니다.

Visual Studio 내장 터미널 사용(2022년부터 지원)

  1. Visual Studio에서 다음을 클릭합니다.보기 > 단말기아니면 단축키를 이용하세요Ctrl + `
  2. 터미널 유형 선택(예: PowerShell 또는 CMD)
  3. 직접 입력 가능dotnetSDK를 확인하거나 동작시키는 명령어


C# 언어

언어 특징

기본 예

시스템 사용;

수업 프로그램 {
    정적 무효 Main() {
        문자열 이름 = "세계";
        Console.WriteLine($"안녕하세요, {이름} 님!");
    }
}

일반적인 응용 분야

고급 기능



CS8618 Non-Nullable이 초기화되지 않았습니다.

경고 내용

CS8618은 다음을 의미합니다. 생성자의 끝에서Null을 허용하지 않음의 속성은 초기화되지 않았으므로 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 = 새 제품 { 이름 = "휴대폰" }; // 좋아요

제안



.NET 프로그램

과학적 표기법을 피하기 위해 부동 소수점을 문자열로 변환

문제 진술

C++/CLI에서 다음을 사용하는 경우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);

코드 형식 지정 지침



.NET 및 std str 트랜스코딩 잘못된 문자

이유

C++/CLI에서는gcnew System::String(stdStr.c_str())~ 할 것이다std::string(대개ANSI / UTF-8인코딩)을 .NET으로 직접System::String,그리고System::String기대는UTF-16
만약에stdStrASCII가 아닌 문자(예: 중국어)가 포함된 경우 잘못된 문자가 나타납니다.

해결 방법 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 유니코드로 올바르게 자동 변환할 수 있습니다.

---

해결 방법 2: std::string이 UTF-8이라고 확신하는 경우 수동으로 변환할 수 있습니다.

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

해결 방법 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::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 Dictionary

기본 소개

.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++를 사용하여 디렉터리의 최신 파일 가져오기

소개

.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을 반환합니다.
}

코드 설명

애플리케이션 시나리오

주의할 점



.NET: System.Reflection

1. System.Reflection이란 무엇입니까?

System.Reflection메타데이터를 검사하고 조작하기 위한 도구를 제공하는 .NET 프레임워크의 네임스페이스로, 개발자가 런타임에 유형, 메서드, 속성 등을 동적으로 검사하고 개체를 동적으로 생성 및 조작할 수 있도록 합니다.

2. 시스템의 목적.반영

3. 공통 시스템.반영 항목

4. 사용예

다음은 용도입니다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

5. 일반적인 애플리케이션 시나리오



System::Management::ManagementClass

사용

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을 반환합니다.
}

일반적인 오류 및 문제 해결

관련 카테고리



Win32_NetworkAdapterConfiguration에서 잘못된 클래스 오류 수정

오류 코드 설명

`System::Management::ManagementClass("Win32_NetworkAdapterConfiguration")`를 사용할 때 오류 코드 `0x80041010`이 발생합니다. 이는 WMI가 클래스를 찾을 수 없음을 나타냅니다. 이는 일반적으로 다음을 의미합니다.

해결 단계

다음은 Windows 10/11용 WMI 저장소를 다시 만드는 완전한 수정 사항입니다.
net stop winmgmt
winmgmt /resetrepository
net start winmgmt

설명하다

수리 후 확인

다음 방법 중 하나를 사용하여 복구가 성공했는지 확인할 수 있습니다.방법 1: PowerShell 쿼리
Get-WmiObject Win32_NetworkAdapterConfiguration
방법 2: WMI 테스트 도구(wbemtest)
  1. Win + R을 누르고 Enter를 누르십시오.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), 매우 인기 있는 .NET HTML 파서입니다. HAP는 C#으로 작성되었지만 C++/CLI를 포함한 모든 .NET 언어에서 참조로 원활하게 사용할 수 있습니다.

HTML 민첩성 팩을 사용하는 단계

  1. C++/CLI 프로젝트를 만듭니다.프로젝트가 C++/CLI 프로젝트(예: CLR 콘솔 애플리케이션 또는 Windows Forms 앱)인지 확인하세요.
  2. Html 민첩성 팩 받기:
  3. 프로그램 코드 구현(C++/CLI):

코드 예시

다음은 로컬 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을 반환합니다.
}

핵심 기술 포인트 설명



.NET C++ HTTP API 서버

HttpListener 클래스 사용

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 C# HTTP API 서버

개요

ASP.NET Core에서 HTTP API 서버를 만들기 위한 표준 프로젝트 유형은 Web API입니다. 최신 .NET 버전(.NET 6 이상) 권장Minimal APIs빠르고 가볍게 API 엔드포인트를 생성합니다.

최소 API는 최소한의 파일과 코드 줄을 사용하여 시작 논리(Startup)와 라우팅 정의(Routing)를 단일 파일로 결합합니다.Program.cs가운데.

프로젝트 설정(CLI 사용)

새로운 최소 API 프로젝트를 생성하려면 .NET CLI(명령줄 인터페이스)를 사용할 수 있습니다.


dotnet new web -n MinimalApiServer
cd MinimalApiServer

프로그램 코드 예: Program.cs

다음은 최소 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();

실행 및 테스트

  1. 서버를 실행합니다:프로젝트 디렉토리에서 실행dotnet run
  2. 테스트 끝점:서버는 일반적으로 기본적으로http://localhost:5000또는https://localhost:7000달리다.

주요 기술 포인트



Chromebook에서 .NET 프로그램을 실행하는 방법

1. Chromebook의 Linux 지원을 사용하여 .NET 실행

대부분의 최신 Chromebook은 Crostini 프로젝트를 통해 Linux 애플리케이션 실행을 지원합니다.

  1. Linux 기능 활성화:
  2. .NET SDK를 설치합니다.
  3. 터미널에서 .NET 애플리케이션을 컴파일하고 실행합니다.

2. 클라우드 개발 환경을 활용하라

클라우드 플랫폼을 사용하면 로컬에 소프트웨어를 설치하지 않고도 Chromebook에서 .NET 프로그램을 실행할 수 있습니다.

3. Docker 컨테이너를 사용하여 .NET 실행

Chromebook은 컨테이너를 사용하여 애플리케이션을 실행할 수 있도록 지원하며 Docker를 통해 .NET 환경을 시작할 수 있습니다.

  1. Linux 환경에 Docker를 설치합니다.
  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 UI 프로그래밍

주요기술

간단한 WinForms 예

시스템 사용;
System.Windows.Forms 사용;

공개 클래스 MyForm : 양식 {
    공개 MyForm() {
        버튼 btn = new Button();
        btn.Text = "클릭하세요";
        btn.Click += (s, e) => MessageBox.Show("버튼을 클릭하셨습니다!");
        컨트롤.추가(btn);
    }

    [STAT스레드]
    정적 무효 Main() {
        애플리케이션.EnableVisualStyles();
        Application.Run(new MyForm());
    }
}

간단한 WPF 예(XAML + C#)

// 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("버튼을 클릭하셨습니다!");
}

간단한 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>
</콘텐츠페이지>

// 메인페이지.xaml.cs
private void OnButtonClicked(객체 전송자, EventArgs e) {
    wait DisplayAlert("알림", "버튼을 클릭하셨습니다!", "확인");
}

UI 기술 선택 조언

기술 사용 플랫폼
WinForms 데스크탑 애플리케이션을 신속하게 개발 Windows
WPF 복잡한 데스크탑 애플리케이션, MVVM 패턴 Windows
MAUI 크로스 플랫폼 애플리케이션 Windows、macOS、Android、iOS
Blazor 웹 프론트엔드 개발 크로스 플랫폼(웹)


.NET은 하위 컨트롤을 탐색하여 일괄적으로 속성을 수정합니다.

다음 예에서는 사용 방법을 보여줍니다.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' 컨트롤은 편집 불가능으로 설정됩니다.



.NET MessageBox

1. 기본 구문 및 일반적인 오버로딩

C++/CLI에서는MessageBox::Show대화 상자를 팝업하는 데 사용되는 정적 메서드입니다. 가장 완벽한 통화 방법에는 메시지 내용, 제목, 버튼 유형, 아이콘 및 기본 버튼이 포함됩니다.

네임스페이스 System::Windows::Forms 사용;

//가장 간단한 디스플레이
MessageBox::Show("작업 완료");

//전체 매개변수 버전
DialogResult 결과 = MessageBox::Show(
    "이 파일을 삭제하시겠습니까?", // 메시지 내용(텍스트)
    "삭제 확인", // 창 제목(캡션)
    MessageBoxButtons::YesNo, // 버튼 조합
    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받은 것은 .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" 표시

일반적인 오류 및 해결 방법



.NET 사용자 정의 입력 대화 상자

1. MessageBox가 Edit Box를 지원하지 않는 이유는 무엇입니까?

System::Windows::Forms::MessageBox"알림" 또는 "경고"를 표시하고 표준 버튼을 통해 사용자 결정을 얻도록 설계되었습니다. 컨트롤 항목을 동적으로 삽입하는 기능은 없습니다. 텍스트를 입력해야 하는 경우 표준 접근 방식은 상속을 받는 것입니다.System::Windows::Forms::Form사용자 정의 대화 상자를 만듭니다.


2. 사용자 정의 InputBox 구현

이는 레이블, 텍스트 상자 및 확인 버튼이 포함된 간단하고 재사용 가능한 범주입니다.

네임스페이스 시스템 사용;
네임스페이스 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;
    }
};

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는 여전히 비트맵에 대한 참조를 보유하고 있으며 릴리스된 리소스는 그림판 중에 액세스됩니다.
소스 스트림이 닫혔습니다. 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+ 비트맵에 해당하는 파일 또는 스트림이 더 이상 존재하지 않습니다. 비트맵 수명 동안 소스가 여전히 유효한지 확인하세요.

디버그 단계

적십자 문제가 자주 발생하지 않을 수 있으므로 다음 순서대로 진행하는 것이 좋습니다.

  1. OnPaint 예외 가로채기: PictureBox 상속, 재정의OnPaint, try/catch를 사용하여 예외를 포착하고 다음으로 출력합니다.Debug.WriteLine, 캡처 후 실행this.Image = null지속적인 적십자 표시를 피하십시오.
  2. 예외 메시지 확인: Visual Studio 출력 창에서 메시지를 관찰합니다. 근본 원인을 찾으려면 위의 오류 메시지 표를 확인하세요.
  3. 비트맵 수명주기 추적: Dispose Bitmap에 할당될 때마다 기록Environment.StackTrace, 누가 언제 리소스를 공개했는지 알아보세요.
  4. 아직 사용 중인 비트맵 폐기 방지: 폐기 전 확인하세요 PictureBox가 여전히 비트맵을 보유하고 있는지 여부, 그렇다면 먼저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(); // 기존 사진을 다시 폐기합니다.
}

Stream에서 비트맵을 생성하는 올바른 방법

// 오류: 스트림이 닫힌 후 비트맵이 유효하지 않습니다.
(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 타이밍 문제일 수 있습니다.


.NET StackTrace에서 메시지 m의 내용 가져오기

문제 배경

스택 추적에 다음 오류 메시지가 나타나면 개발자는 확인해야 할 수 있습니다.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(참조 메시지 m)
{
    시도하다
    {
        //메시지 내용을 기록합니다.
        Console.WriteLine($"메시지 세부 정보: hWnd={m.HWnd}, Msg={m.Msg}, WParam={m.WParam}, LParam={m.LParam}");
        base.WndProc(ref m);
    }
    잡기 (예외예외)
    {
        Console.WriteLine($"예외: {ex}");
        던지다; // 원래 예외 동작을 유지합니다.
    }
}
    

이 코드는 메시지가 수신될 때마다 관련 정보를 기록하고 개발자가 이를 추가로 분석할 수 있도록 합니다.

2. Dispose 메서드에 예외 처리 추가

에서 오류가 발생하는 경우Dispose메소드 내에서 예외 처리를 추가하여 관련 정보를 확인할 수 있습니다.

보호된 재정의 void Dispose(bool disposing)
{
    시도하다
    {
        만약 (처분)
        {
            // 리소스 해제
        }
        base.Dispose(처리);
    }
    잡기 (예외예외)
    {
        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개체의 내용입니다.

메시지 개체의 주요 속성

주의할 점



.NET C++에서 Form.WmClose(Message& m) 재정의

문제 배경

.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을 반환합니다.
}
    

코드 설명

1. WmClose 메서드

덮어쓰기 시뮬레이션WmClose여기에서 창 닫기 동작을 사용자 정의하는 방법입니다. 확인 대화 상자를 사용하여 사용자에게 창을 닫을 것인지 묻습니다.

2. WndProc 재정의

덮어쓰기WndProc가로채는 방법WM_CLOSE메시지를 전송하고 이를 사용자 정의에 위임합니다.WmClose방법.

3. 계속해서 다른 메시지를 처리하세요.

방해받지 않고WM_CLOSE기본 클래스를 호출하는 경우WndProc다른 메시지를 처리하는 방법입니다.

주의할 점



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

사용 지침

InvokeRequiredSystem::Windows::Forms::Control이 속성은 현재 실행 스레드가 컨트롤 항목이 속한 UI 실행 스레드인지 여부를 확인하는 데 사용됩니다. 백그라운드 스레드에서 UI 컨트롤에 액세스할 때 다음을 사용해야 합니다.Invoke작업을 다시 UI 스레드로 마샬링합니다.

전체 예: PictureBox 그림 설정

네임스페이스 시스템 사용;
네임스페이스 System::Windows::Forms 사용;
네임스페이스 System::드로잉 사용;

참조 클래스 ImageHelper {
공개:
    정적 무효 SetImageSafe(PictureBox^ pPictureBox, Bitmap^ b) {
        if (pPictureBox == nullptr)
            반환;

        if (pPictureBox->InvokeRequired) {
            // MethodInvoker를 사용하여 동일한 함수를 호출하지만 UI 스레드에서 실행합니다.
            pPictureBox->호출(
                gcnew MethodInvoker(gcnew 액션(ImageHelper::InvokeCallback), 
                gcnew Tuple(pPictureBox, b))
            );
        } 그렇지 않으면 {
            ApplyImage(pPictureBox, b);
        }
    }

비공개:
    // 마샬링된 함수 프록시
    정적 무효 InvokeCallback(Object^ 상태) {
        튜플^ args = static_cast^>(상태);
        ApplyImage(args->Item1, args->Item2);
    }

    // 이미지 로직 설정의 실제 실행
    정적 무효 ApplyImage(PictureBox^ pPictureBox, Bitmap^ b) {
        {를 시도해보세요
            if (b != nullptr) {
                if (pPictureBox->이미지 != nullptr)
                    pPictureBox->이미지 삭제;
                pPictureBox->이미지 = b;
            }
        }
        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, b });
    } 그렇지 않으면 {
        SetImageInternal(pPictureBox, b);
    }
}

void SetImageInvoker(Object^ 보낸 사람, EventArgs^ e) {
    // 이 버전을 사용하지 않으면 무시하고 전체 참조로 유지할 수 있습니다.
}

void SetImageInternal(PictureBox^ pPictureBox, Bitmap^ b) {
    {를 시도해보세요
        if (b != nullptr) {
            if (pPictureBox->이미지 != nullptr)
                pPictureBox->이미지 삭제;

            pPictureBox->이미지 = b;
        }
    }
    catch (System::Exception^ ex) {
        Console::WriteLine("이미지 설정 실패: " + ex->Message);
    }
}

단순화된 버전(익명 위임 권장)

`pPictureBox->Image = b;`를 빠르게 보호하려면 다음 작성 방법이 더 직접적이고 효과적입니다.
네임스페이스 시스템 사용;
네임스페이스 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);
        }
    }
}

추가 정보



Blazor를 사용하여 통합 언어 프런트 엔드 개발 환경 구축

블레이저란 무엇인가요?

Blazor는 개발자가 C# 및 Razor 구문을 사용하여 대화형 웹 애플리케이션을 만들 수 있도록 Microsoft에서 출시한 프런트 엔드 프레임워크입니다. JavaScript를 사용하지 않고도 C# 코드를 브라우저에서 실행할 수 있습니다.

개발자 혜택

블레이저 모드

.NET과의 통합

Blazor는 ASP.NET Core를 원활하게 통합하고 기존 .NET 애플리케이션과 결합할 수 있으며 종속성 주입, 라우팅, 구성 요소 아키텍처 및 기타 기능을 지원합니다.

배포 방법

요약

Blazor는 .NET을 주요 기술로 사용하는 개발자를 위해 Node.js의 통합 JavaScript 언어에 해당하는 솔루션을 제공하여 프런트엔드와 백엔드 모두에서 C#을 사용할 수 있도록 하여 보다 일관된 개발 경험을 제공합니다.



.NET MAUI를 사용하여 크로스 플랫폼 애플리케이션 구축

.NET MAUI란 무엇입니까?

.NET MAUI(Multi-platform App UI)는 Microsoft에서 출시한 크로스 플랫폼 애플리케이션 프레임워크입니다. C# 및 XAML을 사용하여 코드를 한 번 작성하고 이를 Windows, Android, iOS 및 macOS에 배포할 수 있습니다.

개발자 혜택

지원되는 플랫폼

Blazor 하이브리드 모드

.NET MAUI의 Blazor 하이브리드 모드를 사용하면 기본 애플리케이션에 웹 UI를 포함할 수 있으므로 개발자는 Razor 구성 요소를 사용하여 기본 API에 계속 액세스하면서 크로스 플랫폼 사용자 인터페이스를 개발할 수 있습니다.

통합 및 배포

요약

.NET MAUI는 현재 가장 완벽한 .NET 크로스 플랫폼 솔루션입니다. Blazor와 결합하여 웹, 데스크톱 및 모바일 플랫폼을 위한 통합 애플리케이션 개발을 달성하고 .NET 개발자에게 언어와 기술이 일관되는 ​​완전한 경험을 제공할 수 있습니다.



ASP.NET Core와 .NET MAUI 비교

위치

지원 플랫폼

개발 언어

UI와 상호작용

적용 가능한 시나리오

완성

.NET MAUI는 API 호출을 통해 ASP.NET Core에서 생성된 백엔드 서비스와 통합되어 완전한 "프런트 엔드 앱 + 백엔드 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">
        <라벨 x:Name="counterLabel"
               Text="아직 버튼을 클릭하지 않았습니다."
               글꼴 크기="24"
               수평 옵션="가운데" />

        <버튼 텍스트="나를 클릭하세요"
                Clicked="OnCounterClicked"
                수평 옵션="가운데" />
    </VerticalStackLayout>
</콘텐츠페이지>

백엔드 코드: MainPage.xaml.cs

네임스페이스MauiApp;

공개 부분 클래스 MainPage : ContentPage
{
    정수 개수 = 0;

    공개 메인페이지()
    {
        초기화구성요소();
    }

    개인 무효 OnCounterClicked(객체 보낸 사람, EventArgs e)
    {
        카운트++;
        counterLabel.Text = $"{count}번 클릭하셨습니다";
    }
}

실행방법

요약

이 간단한 예는 .NET MAUI의 크로스 플랫폼 기능을 보여줍니다. 개발자는 XAML 및 C# 코드를 한 번만 작성하면 여러 플랫폼에서 동시에 실행할 수 있습니다.



Visual Studio에서 .NET MAUI 프로젝트 만들기

1단계: 필요한 도구 설치

2단계: 새 MAUI 프로젝트 만들기

  1. 비주얼 스튜디오 열기
  2. "새 프로젝트 만들기"를 클릭하세요.
  3. "를 검색하여 선택하세요..NET MAUI App"주형
  4. "다음"을 클릭하고 프로젝트 이름과 위치를 입력하세요.
  5. 프로젝트 초기화를 완료하려면 "Create"를 클릭하세요.

3단계: 프로젝트 구조 이해

4단계: 애플리케이션 실행

추가 정보

요약

Visual Studio를 사용하여 .NET MAUI 프로젝트를 생성하려면 몇 단계만 거치면 되며, 하나의 코드 세트를 동시에 여러 플랫폼에 배포할 수 있으므로 최신 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"
                         VerticalOptions="가운데">

        <라벨
            Text=".NET MAUI에 오신 것을 환영합니다!"
            SemanticProperties.HeadingLevel="레벨1"
            글꼴 크기="32"
            수평 옵션="가운데" />

        <버튼
            x:이름="counterBtn"
            Text="나를 클릭하세요!"
            Clicked="OnCounterClicked"
            수평 옵션="가운데" />

    </VerticalStackLayout>

</콘텐츠페이지>

해당 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(글꼴 =>
            {
                글꼴.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                글꼴.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            });

#if 디버그
        builder.Logging.AddDebug();
#endif

        return builder.Build();
    }
}

기능 설명

일반적인 확장

요약

MauiProgram.csASP.NET Core와 마찬가지로Startup.cs또는Program.cs, 애플리케이션 시작 및 서비스 등록을 위한 주요 설정 센터입니다. 대규모 MAUI 애플리케이션을 개발할 때 이 파일을 확장하여 DI, 로깅, 구성 요소 설정 등을 추가하는 것이 매우 일반적입니다.



.NET C++ 프로그램을 Ubuntu에 배포

머리말

Ubuntu 시스템에 .NET C++(C++/CLI 또는 .NET 기반 C++ 애플리케이션) 배포는 프로젝트 유형에 따라 별도로 처리해야 합니다. 순수 C++/CLI(.NET Framework 기반)를 사용하는 경우 Linux에서 직접 실행할 수 없으며 다음을 사용해야 합니다..NET 6/7/8용 크로스 플랫폼 기술(예: C++/CLR → C# 또는 .NET 상호 운용성을 갖춘 C++/Native 사용)

배포 개요

애플리케이션의 특성에 따라 배포는 세 가지 시나리오로 나눌 수 있습니다.

  1. 순수 C++ 애플리케이션:g++로 컴파일하고 Ubuntu에서 직접 실행합니다.
  2. .NET Managed C++ (C++/CLI):.NET 6 이상에서 지원되는 Core용 C# 또는 C++/CLI에 맞게 재설계해야 합니다(Windows에서만 지원).
  3. 하이브리드 애플리케이션:주로 C#, C++에서는 동적 함수 라이브러리(.so) 형태로 성능 모듈을 제공합니다.

환경 설치

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

C#(.NET)에서 C++ 라이브러리 호출

~에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 시스템에서 실행할 수 있습니다.

# 도커파일 예시
mcr.microsoft.com/dotnet/runtime:8.0에서
WORKDIR/앱
복사 ./publish .
복사 ./native/libadd.so /usr/lib/
진입점 ["./MyApp"]

컨테이너 빌드 및 실행:

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

주의할 점

요약

.NET C++ 프로그램을 Ubuntu에 배포하는 실행 가능한 방법은 일반적으로 다음과 같습니다.C# + C++ 네이티브 라이브러리"구조입니다. 원래 프로그램이 C++/CLI에 의존하는 경우 크로스 플랫폼 .NET을 지원하도록 다시 설계해야 합니다. 가장 권장되는 방법은 Docker와 함께 .NET 8 + 네이티브 C++ 모듈을 사용하여 안정적이고 이식 가능하며 일관된 배포 프로세스를 달성하는 것입니다.




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