Dotnet .NET · C# 메모 — async/await 에서 4.0 으로 내려오기, C++ DLL 마샬링, WPF 컨트롤 PNG 저장 2015 · 10 · 16
2 min read
페이퍼
목차 2015년에 윈도우 앱을 하나 만들면서 남겼던 메모 세 개를 합쳤다.
async/await 로 짰다가 4.0 으로 내려온 이야기 윈도우 앱을 개발할 일이 생겼다. .NET 4.5부터 async 문법이 새로 들어갔다 해서
이왕 하는 거 4.5.2 로 만들기로 했다. 엄청나게 편리하다.
async, await 두 개가 중요하다. 특히 UI 프로그램에서
background thread 와 main thread 의 동기화를 쉽게 해준다.
아래는 id/pwd 를 입력받아서 서버 통신으로 인증하는 코드다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private async void btnLogin_Click ( object sender , RoutedEventArgs e )
{
string id = tbEmail . Text ;
string pwd = tbPassword . Password ;
SetControlEnableState ( false );
bool isSuccess = await Task . Run < bool >(() =>
{
try
{
// 여기가 네트워크 통신을 하는 부분이다.
_dataCore . DoLogin ( id , pwd );
return true ;
}
catch ( Exception x )
{
ErrorHandler . ErrorDump ( x , true );
return false ;
}
});
if (! isSuccess )
{
SetControlEnableState ( true );
return ;
}
}
보면 알겠지만 클라이언트 이벤트에서 바로, 별도 쓰레드 동기화 없이 로그인 처리를 다 끝냈다.
Invoke 니 Dispatcher 니 하는 게 하나도 없다.
그리고 테스트를 진행하는데 .NET Framework 4.5 깔린 PC 가 많이 없었다.
그래서 4.0 으로 BackgroundWorker 를 써서 재개발했다.
그지 같네.. 화면이 세 개인 프로그램이라 그나마 다행이었다.
교훈이라면, 문법이 편한 것보다 배포 대상 PC 의 런타임 버전을 먼저 확인 해야 한다는 것.
C# 에서 C++ DLL 에 LPCTSTR 넘기기 C++ 로 된 dll 을 C# 에서 호출할 때의 예제다.
C++ 쪽 1
extern "C" __declspec ( dllexport ) int test ( LPCTSTR szFileName );
1
2
3
int test ( LPCTSTR szFileName ) {
return 0 ;
}
C# 쪽 1
2
3
4
5
6
7
8
9
[DllImport("sampleLib.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int test ( [ MarshalAs ( UnmanagedType . LPWStr )] string szFileName );
private void button2_Click ( object sender , EventArgs e )
{
string szFileName = @"c:\filename.txt" ;
int result = test ( ticketName );
Debug . WriteLine ( "test=" + result );
}
CallingConvention = CallingConvention.Cdecl 와 [MarshalAs(UnmanagedType.LPWStr)]
이 두 개가 중요하다. 둘 중 하나만 빠져도 호출이 깨지거나 문자열이 깨진다.
반대로 C++ 에서 string 을 받을 때 IntPtr 로 받아서 변환한다.
1
2
3
4
IntPtr ptr = test ( "111111" );
string data = Marshal . PtrToStringAnsi ( ptr );
// 꼭 해제 한다
Marshal . FreeHGlobal ( data );
해제를 빼먹으면 샌다.
WPF 컨트롤을 PNG 로 저장 예전에 하던 대로 System.Drawing 을 쓰려고 했는데, WPF 에 이런 기능이 이미 있었다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// uiPage.ren
public void DoPageToPng ( string fileName )
{
RenderTargetBitmap rtb = new RenderTargetBitmap (( int ) uiPage . ActualWidth , ( int ) uiPage . ActualHeight , 96 , 96 , PixelFormats . Pbgra32 );
rtb . Render ( uiPage );
PngBitmapEncoder png2 = new PngBitmapEncoder ();
png2 . Frames . Add ( BitmapFrame . Create ( rtb ));
using ( MemoryStream stream = new MemoryStream ())
{
png2 . Save ( stream );
using ( System . Drawing . Image image = System . Drawing . Image . FromStream ( stream ))
{
image . Save ( fileName );
}
}
}
RenderTargetBitmap 이 핵심이다. 화면에 보이는 컨트롤을 그대로 비트맵으로 렌더링해준다.
정리 async/await 는 UI 쓰레드 동기화를 확 줄여준다. 다만 배포 대상 런타임을 먼저 확인할 것 C++ dll 호출은 CallingConvention.Cdecl + MarshalAs(UnmanagedType.LPWStr) 받은 IntPtr 은 Marshal.FreeHGlobal 로 꼭 해제 WPF 컨트롤 캡처는 RenderTargetBitmap