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
}
}
댓글남기기