C# WPF - 여러가지 문법, 로직 정리

업데이트:

C# WPF 개발

동기화 키워드

Lock

쓰레드별로 접근 제한을 걸 수 있다.

private System.Object.lockThis = new System.Object();

public void Process()
{
    lock (lockThis)
    {

    }
}


Monitor

Enter/Exit 메서드로 하나의 쓰레드만 접근을 허용할 수 있다.

System.Object obj = (System.Object)x;
System.Threading.Monitor.Enter(obj);
try {
    
} finally {
    System.Threading.Monitor.Exit(obj);
}


Mutex

monitor와 달리 프로세스 간에 스레드를 동기화 할 수 있다.

static Mutex m_mutex = new Mutex();

static void Main(string[] args)
{
    Thread thread1 = new Thread(new ThreadStart(ThreadProc));
    thread1.Start();

    Thread thread2 = new Thread(new ThreadStart(ThreadProc));
    thread2.Start();
}

static void ThreadProc()
{
    m_mutex.WaitOne();
    Console.WriteLine($"{Thread.CurrentThread.GetHashCode()}");
    m_mutex.ReleaseMutex();
}


쓰레드 동작

CheckBeginInvokeOnUI

UI 쓰레드에서 작업 실행

  • UI 쓰레드에서 호출: UI 쓰레드 즉시 실행 (동기)
  • 다른 쓰레드에서 호출: UI 쓰레드가 대기열에 추가되어 작업 쓰레드와 (비동기적) 실행
DispatcherHelper.CheckBeginInvokeOnUI(() => { });


RunAsync

UI 쓰레드를 호출하여 작업 쓰레드와 비동기적 실행

DispatcherHelper.RunAsync(() => { });
  • CheckBeginInvokeOnUI vs RunAsync

차이 - UI 쓰레드에서 호출할 때, 동기/비동기 실행여부


Invoke

  • Delegate.Invoke

동일한 스레드에서 동기적으로 실행

  • Control.Invoke

UI 쓰레드에서 동기적으로 실행되며 작업 쓰레드는 UI 작업이 끝날때까지 대기


BeginInvoke

  • Delegate.BeginInvoke

별도의 쓰레드가 작업 쓰레드와 (비동기적) 실행

  • Control.BeginInvoke

UI 쓰레드가 작업 쓰레드와 (비동기적) 실행


구조

Locator.cs 싱글톤 구조

아래의 코드는 이전의 인터페이스를 이후 클래스가 구현한다고 등록

ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

// Services
SimpleIoc.Default.Register<IClass1, Class1>();
SimpleIoc.Default.Register<IClass2, Class2>();
...


아래의 코드는 해당 객체 인스턴스를 가져옴.

인터페이스로 되어있지만 일반 클래스로 정의할 경우도 그대로 가능.

인터페이스형으로 불러올 수 있는 이유는 위의 코드에서 이미 구현 등록이 되었기 떄문

인스턴스가 없을 경우, 생성자를 통해 자동 생성하여 인스턴스를 생성

public static IClass1 Class1 => ServiceLocator.Current.GetInstance<IClass1>();
public static IClass2 Class2 => ServiceLocator.Current.GetInstance<IClass2>();
...


인스턴스가 없을 경우, 생성자를 통해 자동 생성하여 인스턴스를 생성

  • 생성자의 인자들이 등록되어있는 클래스 객체로 되어있다면 인스턴스를 호출할 때 인자 객체를 따로 넣지 않아도 알아서 등록된 객체인스턴스를 할당하여 생성자를 통해 인스턴스를 생성하게됨
public MainViewModel(
    IClass1 class1,
    IClass2 class2,
    IClass3 class3,
    IClass4 class4,
    IClass5 class5,
    IClass6 class6)
{

}
SimpleIoc.Default.Register<IClass1, Class1>();
SimpleIoc.Default.Register<IClass2, Class2>();
SimpleIoc.Default.Register<IClass3, Class3>();
SimpleIoc.Default.Register<IClass4, Class4>();
SimpleIoc.Default.Register<IClass5, Class5>();
SimpleIoc.Default.Register<IClass6, Class6>();


Nuget Package 설치

Install-Package ...절대경로...\누켓 파일 이름.nupkg


Using 문

try - finally 문과 같다.

using 문 내에서 실패하더라도, 반드시 Dispose() 메소드를 호출한다.

// using 문 
using (Font font1 = new Font("Arial", 10.0f)) 
{ 
    byte charset = font1.GdiCharSet; 
} 

// try - finally block 으로 바꾸면... 
Font font1 = new Font("Arial", 10.0f); 
try 
{ 
    byte charset = font1.GdiCharSet; 
} 
finally 
{ 
    if (font1 != null) 
        ((IDisposable)font1).Dispose(); 
}


프로세스 통신

프로세스 송신

1. 타겟 프로세스 메모리 로드

MemoryMappedFile.OpenExising("메모리 명");

2. 메모리에 매핑

MemoryMappedFile.CreateViewAccessor()

3. 읽어서 handle IntPtr 변수에 저장

accessor.Read<IntPtr>(0, out var handle); 

4. 읽은 데이터 크기만큼 메모리 포인터 주소 할당

var memoryTarget = Marshal.AllocHGlobal(bytes.Length);

5. 메모리 주소에 데이터 복사

Marshal.Copy(bytes, 0, memoryTarget, bytes.Length);

6. 전송 구조체 변수 생성

var copyDataStruct = new Win32API.COPYDATASTRUCT
{
    dwData = new IntPtr(1000), // fix 10000 
    cbData = bytes.Length,
    lpData = memoryTarget
};

7. 구조체 크기만큼 메모리 주소 할당

var tempPtr = Marshal.AllocHGlobal(Marshal.SizeOf(copyDataStruct.dwData) + Marshal.SizeOf(copyDataStruct.cbData) + copyDataStruct.cbData);

8. 메모리에 구조체 주소값 할당

Marshal.StructureToPtr(copyDataStruct, tempPtr, true);

9. 데이터 전송

SendMessage(handle, Win32API.WM_COPYDATA, IntPtr.Zero, tempPtr);


프로세스 수신

1. 현재 WPF 창 핸들러 생성

var handle = new WindowInteropHelper(this).Handle

2. 현재 프로세스 메모리 생성

MemoryMappedFile.CreateNew("메모리 명");

3. 메모리에 매핑

MemoryMappedFile.CreateViewAccessor()

4. 매핑된 메모리에 현재 창 메모리 값 할당

write.Write<IntPtr>(0, ref handle);

5. Win32의 WPF 컨텐츠에 핸들러 할당

_hwndSource = HwndSource.FromHwnd(handle);

6. 수신 등록

_hwndSource?.AddHook(WndProc);


WndProc 재정의

7. 수신 데이터 형식 확인

if (msg == WM_COPYDATA)

8. 주소 값에서 구조체로 변환 후 할당

var receivedData = Marshal.PtrToStructure<Win32API.COPYDATASTRUCT>(lparam);

9. 수신 데이터 길이만큼 버퍼 할당

var receivedBuffer = new byte[receivedData.cbData];

10. 버퍼에 데이터 할당

Marshal.Copy(receivedData.lpData, receivedBuffer, 0, receivedData.cbData);

11. 결과 값 변환

result = Encoding.UTF8.GetString(receivedBuffer);


서버 클라이언트 통신

//서버코드
static void Main(string[] args)
{
    Console.WriteLine("Memory mapped file server started");
 
    using (var file = MemoryMappedFile.CreateNew("myFile", int.MaxValue))
    {
        var bytes = new byte[24];
        for (var i = 0; i < bytes.Length; i++)
            bytes[i] = (byte)(65 + i);
 
        using (var writer = file.CreateViewAccessor(0, bytes.Length))
        {
            writer.WriteArray<byte>(0, bytes, 0, bytes.Length);
        }
        Console.WriteLine("Run memory mapped file reader before exit");
        Console.WriteLine("Press any key to exit ...");
        Console.ReadLine();
    }
}
 
//클라이언트코드
static void Main(string[] args)
{
    Console.WriteLine("Memory mapped file reader started");
 
    using (var file = MemoryMappedFile.OpenExisting("myFile"))
    {
        using (var reader = file.CreateViewAccessor(0, 24))
        {
            var bytes = new byte[24];
            reader.ReadArray<byte>(0, bytes, 0, bytes.Length);
 
            Console.WriteLine("Reading bytes");
            for (var i = 0; i < bytes.Length; i++)
                Console.Write((char)bytes[i] + " ");
 
            Console.WriteLine(string.Empty);
        }
    }
 
    Console.WriteLine("Press any key to exit ...");
    Console.ReadLine();
}


데이터 타입

IntPtr

포인터나 핸들을 나타냄, 메모리 주소를 가르킴

Marshal.PtrToStructure

메모리 데이터들을 구조체로 변경

Marshal.StructureToPtr

구조체를 메모리 데이터들로 변경

Marshal.Copy

메모리 포인터 <-> 배열 간의 데이터 복사

Marshal.AllocHGlobal

메모리에서 빈 메모리를 할당

Marshal.FreeHGlobal

메모리 해제


기타

extern 변수

외부에 있는 변수를 사용하겠다는 의미

extern int a; // 외부 변수를 가져와서 할당
int b; // 내부 변수 할당


윈도우 이벤트 가로채기

윈도우에서 발생하는 여러가지 이벤트들을 처리할 수 있음

ComponentDispatcher.ThreadFilterMessage += ComponentDispatcher_ThreadFilterMessage;
void ComponentDispatcher_ThreadFilterMessage(ref MSG msg, ref bool handled)
{
    if (msg.message == "해당 메시지")
    {
        // To do
    }
}

태그: , ,

카테고리:

업데이트:

댓글남기기