2010년 1월 6일 수요일

[.NET Compact Framework-C#] 화면캡쳐-CopyFromScreen

.NET Compact Framework 에서 생략된 유용한 그래픽스 함수 중 하나인 CopyFromScreen...
기본 컨트롤들은 투명처리가 되지 않기 때문에 완벽하게 커버할 수 있는건 아니지만 CopyFromScreen이 있다면 팝업되는 컨트롤들은 어느정도 보이기에는 커버할 수 있다. 하지만 아쉽게도 컴팩트 버전에서는 생략되었다.
직접 개발한것은 아니지만 아래 소스는 Win32 API를 이용하여 같은 작동을 하도록 구현하였다.
출처는 기억이 잘 나지 않는다.(-_-)

작동 원리는 Device Context를 얻어오는 Win32 API인 GetDC 함수에 윈도우 핸들을 NULL을 넣으면 전체 화면의 Device Context를 얻게 되는데 같은 Win32 API인 BitBlt로 사용자 비트맵의 DC로 카피를 하는 방법으로 구현하였다.

자세한 사항은 소스를 참고하기 바란다.


응용을 하자면 BitBlt의 좌표를 조절하여 부분 화면을 캡처할 수도 있으며 Win32 API중의 하나인 AlphaBlend 함수를 이용하여 배경의 명도를 조절하거나 반투명 처리가 된 이미지 추출도 가능하니 관심있는 개발자들은 시도해보길 바란다.

[.NET Compact Framework-C#] Control Double Buffering

컨트롤 커스터마이징을 하다 보면 요구사항에 따라 Flicker와 같이 부드럽게 동적으로 움직이는 컨트롤이 필요할 때가 있다. 컨트롤 클래스를 상속받아 열심히 오너 드로잉을 해 보면 알겠지만 Invalidate를 이용하여 다시 그리기를 시도하면 컨트롤이 아주 작지 않는 이상 깜박 거림을 볼 수 있을 것이다. 이를 방지하기 위해서 타이머에서 그래픽스 객체를 직접 얻어다가 그려주는 방식을 사용할 수도 있겠지만 보편적인 Invalidate를 사용하는 방법을 소개해보고자 한다.
결론 부터 말하자면 수동적인(?) 더블 버퍼링을 사용하는 방법인데 다음과 같이 정리할 수 있다.

1. 컨트롤 크기와 같은 이미지를 생성한다.
2. 이미지의 그래픽스 객체를 얻어 OnPaint 나 OnPaintBackground 내의 그리기 루틴을 이미지에 그린다.
3. 컨트롤 자체의 그래픽스 객체에 이미지를 뿌려준다.

자세한 내용은 아래 소스를 참고하면 되지만 포인트 별 부연설명을 하자면 다음과 같다.


1. 컨트롤 사이즈 변경(OnResize)에서 컨트롤 크기와 같은 이미지를 생성한다. 이 때 screenBuffer, graphics, paintEvent는 클래스 멤버이다.

private Bitmap screenBuffer;
private Graphics graphics;
private PaintEventArgs paintEvent;

protected override void OnResize(EventArgs e)
{
base.OnResize(e);

if (graphics != null)
{
graphics.Dispose();
}

if (screenBuffer != null)
{
screenBuffer.Dispose();
}

screenBuffer = new Bitmap(Width, Height);
graphics = Graphics.FromImage(screenBuffer);
paintEvent = new PaintEventArgs(graphics, ClientRectangle);
}


2. OnPaint, OnPaintBackground 를 오버라이딩 하여 그리기 중복 방지를 위하여 하위클래스에서 상속 받을 수 없게 한다.

protected override sealed void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
}

protected override sealed void OnPaintBackground(PaintEventArgs e)
{
base.OnPaintBackground(e);
}


3. OnDraw 메소드를 가상함수로 정의하고 컨트롤 그리기의 공통행위(?)를 구현한다. 예제 소스에서는 BackColor로 화면을 클리어 하는 루틴을 구현해 넣었다.

protected virtual void OnDraw(PaintEventArgs e)
{
e.Graphics.Clear(BackColor);
}


4. OnPaint, OnPaintBackground 에서 base 호출을 주석처리하고 둘 중 한군데에 screenBuffer를 뿌리게 한다. 예제 소스에서는 OnPaintBackground에서 뿌리게 하였다.

protected override sealed void OnPaint(PaintEventArgs e)
{
//base.OnPaint(e);
}

protected override sealed void OnPaintBackground(PaintEventArgs e)
{
//base.OnPaintBackground(e);
if (this.graphics != null && screenBuffer != null)
{
OnDraw(paintEvent);
e.Graphics.DrawImage(screenBuffer, 0, 0);
}
}


5. 컨트롤을 더 이상 사용하지 않을 경우를 생각해서 다음과 같이 Dispose 함수를 오버라이딩 하여 구현한다.

protected override void Dispose(bool disposing)
{
if (disposing)
{
if (graphics != null)
{
graphics.Dispose();
}

if (screenBuffer != null)
{
screenBuffer.Dispose();
}
}
base.Dispose(disposing);
}

이 방법을 사용하면 디자이너에서도 사용할 수 있으므로 디자이너에서 컨트롤 배치를 하는 개발자라면 유용하게 쓰일 듯 하다.

2010년 1월 5일 화요일

[.NET Compact Framework-C#] 리소스 이미지 속도개선

프로젝트를 진행하다 보면 .NET Compact Framework 에서 기본적으로 제공되는 컨트롤들 만으로는 고객의 입맛을 맞출 수 없다. 그러다 보면 이미지를 처 바르는(-_-) 행태를 보이게 되는데 사용자에 따라 파일로 된 이미지를 동적으로 로딩하던지 아니면 리소스에 추가하여 사용하던지 할 것이다.

이번 프로젝트에서는 후자를 사용했는데(까놓고 얘기하자면 사용하기 편하다는 이유 밖에 없지만) 이미지가 조금만 많아져도 로딩이 너무 느려지는 현상이 있다. 게다가 리소스 디자이너에서 자동 생성하는 코드가 static 으로 되어 있어 한번 로딩하면 메모리상에서 사라지지도 않는다.

메모리에서 사라지지도 않는데 느리다니... 이는 정말 어처구니 없는 리소스 관리자인듯... 리소스 디자이너를 손 대봤자 Visual Studio가 원상복귀 할것이 뻔하고... 메모리에서 사라지지 못하게 하는건 어쩔 수 없다 치더라도 로딩 속도를 줄여야겠다고 생각했다.

방법은 간단하다. 리소스를 사용하는 클래스에서 이미지 이름으로 생성되는 함수를 static image 멤버에 미리 할당하고 사용하면 static 로딩시에는 같은 속도를 내겠지만 리소스의 데이터를 해석해서 Image 로 만드는 과정을 반복하지 않아 재 사용 시 속도를 줄일 수 있다. 아래는 기존의 느린 방법과 새로운 개선된 방법에 대한 예시이다.

1. 기존 방법

public class Test
{
private Image image;
public Test()
{
image = Test.Properties.Resources.test_image;
}
}


2. 개선된 방법

public class Test
{
private static Image text_image = Test.Properties.Resources.test_image;
private Image image;
public Test()
{
image = text_image;
}
}


리소스 이미지를 사용하고 반복 로딩을 많이 할 경우 위와 같은 방법을 사용하면 확실히 속도 개선을 볼 것이다.

덧붙여 말하자면 조금 어처구니 없는것은... 디자이너에서 바로 이미지를 삽입하면 리소스에서 바로 읽어오는 형태(1번과 같은 방법으로)로 코드가 자동완성되는데... MS는 이런걸 알고도 리소스 매니저를 만든것인지...

[.NET Compact Framework-C#] Native DLL(Marshaling) 사용 시 주의할 점

.NET Compact Framework 을 사용하여 프로젝트를 진행하는 사람이라면 느끼겠지만... 너무 느리고... 너무 부족하다... 필자도 이번 프로젝트를 진행하면서 JAVA와의 코드 전환을 용이하게 하려고 .NET Compact Framework 을 이용했지만 몇달전 쯤 부터 "C/C++ 로 개발할 것을..." 하는 후회를 밥먹듯이 하고 있다.

여튼... 앞에 너무 느리고 너무 부족하다는 말을 했는데... 이번에는 너무 부족한 부분에 대해 얘기해 볼까 한다.  그냥 PC에서 돌아가는 .NET Framework은 그래도 어느 정도 API가 있어서 쓸만할지 모르지만 .NET Compact Framework 에서 먼가를 하려고 하면 VS의 자동완성 UI에서 함수를 찾을 수 없는 경우가 부지기수다. MSDN을 찾아보면 결국 돌아오는 답은...

".NET Framework에서 모든 플랫폼의 모든 버전을 지원하지는 않습니다. 지원되는 버전의 목록은 시스템 요구 사항을 참조하십시오."

결국 대부분의 C# 개발자들은 이 부족함을 극복하기 위하여 Native DLL을 사용하게 된다.  이 때 능숙하지 않는 개발자라면 데이터 타입을 어떻게 맞춰야 하는지... 즉 마샬링을 어떻게 해야 하는지에 대한 문제에 고민을 하게 된다. (여기서 능숙하다는 표현은 프로그래밍 언어에 대한 전반적인 이해가 아니라 마샬링에 대해 능숙함을 나타낸다.) 이에 대해 구글링을 해보고... 책도 찾아보고... 난이도가 높은 구조체 마샬링도 해보고... 하지만... 불규칙적으로 Native Exception(0x80000002)을 토해내며 죽는 어플리케이션...

마샬링에 대해 정통한 사람이 이 글을 읽으면 비웃겠지만... 필자는 아래와 같은 방법을 사용하여 오류를 피해갔다.

예를 들어 다음과 같은 C 코드로 abc.dll 을 만들었다고 가정한다.

typedef struct ABC
{
int a;
int b;
int c;
} ABC;

ABC* g_abc = NULL;

void SetABC(ABC* abc)
{
g_abc = abc;
}

void SetA(int a)
{
if (!g_abc)
{
return;
}
g_abc->a = a;
}

void SetB(int b)
{
if (!g_abc)
{
return;
}
g_abc->b = b;
}

void SetC(int c)
{
if (!g_abc)
{
return;
}
g_abc->c = c;
}

int GetA()
{
if (!g_abc)
{
return 0;
}
return g_abc->a;
}

int GetB()
{
if (!g_abc)
{
return 0;
}
return g_abc->b;
}

int GetC()
{
if (!g_abc)
{
return 0;
}
return g_abc->c;
}


abc.dll 을 다음과 같이 C#에서 사용한다.

[DllImport("abc.dll")]
private static extern void SetABC(IntPtr pABC);
[DllImport("abc.dll")]
private static extern void SetA(int a);
[DllImport("abc.dll")]
private static extern int GetA();
...
IntPtr pAbc = Marshal.AllocHGlobal(12);
// 관리되지 않는 메모리 사용구간: 시작
SetABC(pABC);
SetA(10);
int a = GetA();
...
// 관리되지 않는 메모리 사용구간: 끝
Marshal
.FreeHGlobal(pABC);

위 코드에서 일반적인 사용방법과 다른 부분이 있다면 구조체나 버퍼를 할당할 필요가 있을 때 Marshal.AllocHGlobal(int)를 사용하고 해제할 때 Marshal.FreeHGlobal(IntPtr)을 사용했다는 점이다. 메모리 할당 시에 12를 사용한것은 C에서 sizeof(ABC) 하면 나오는 값이다.

원리를 살펴보자면... C#에서 사용하는 관리되지 않는 메모리와 관리되는 메모리의 차이점을 이용한건데...
관리되지 않는 Native DLL내의 함수에 관리되는 메모리 영역의 버퍼를 넣었을 경우 소스에서 명시된 "관리되지 않는 메모리 사용구간"이 수행되는 동안 다른 사용자 쓰레드에 의해 가비지 컬렉션등의 메모리 관리가 이루어졌을 경우 문제가 생길 여지가 있다는 것이다.

위 소스로 해당 문제가 발생하는지는 사실 확인을 하지 못했다. 하지만 공개는 할 수 없고... 유사한 구조를 가진 소스에서 문제가 발생하는 것은 확인하였고 위 방법으로 해결하였다.

1차적으로 Native DLL을 위와 같이 만들면 안되겠지만... 다른 사람이 만들어 배포된것을 사용할 수 밖에 없을 때 마샬링이 찜찜할 경우 위와 같이 사용하면 대부분의 문제는 피해갈 수 있을 것으로 생각된다.

먼가 설명이 조금 허접한 것 같은데 글이 잘못되었거나 이해가 가지 않으면 태클 환영함...-_-

2009년 11월 22일 일요일

피칸파이

이 블로그를 개설한 목적과는 틀리게 개발에 대한 포스트는 처음에 딸랑 하나...
자꾸 잡다한 포스트만 올리게 된다.

 아래 사진은 어제 올만에 만든 호두파이... 대부분의 제빵류는 사서먹는게 더 싼데 이넘은 만들어 먹는게 더 맛있고 싸다.
제일 비싼 재료는 버터... 그 다음에 호두... 호두는 마트에서 1만원 정도에 깐 호두 한봉지 사면 4-5회 이상 사용할 수 있고, 버터는 400g 무염버터(약 6000원) 사면 2번 정도 쓸 수 있다.
이렇게 만든 호두파이의 크기는 지름 20cm 정도?


파이나 타르트류 좋아하시는 분들은 FAT(DOS의 파일 시스템 이름이 아님) 걱정하시길...
돌아다니는 레시피만 봐도 알겠지만 밀가루 반죽을 물로 하는게 아니라 버터로 한다는 사실을...

궁금하신 분은 칼로리 계산해보시길...ㅎㅎㅎ
버터 150g, 깐 호두 한줌, 밀가루 150g, 계란 4개, 아가베시럽 적당량, 등등..(레시피를 보면서 만든게 아니라... 눈대중으로 만든거라... 너무 적당하다..-_-)

2009년 11월 20일 금요일

ELECOM EHP-IN210WH

항상 그렇지는 않지만 노트북 주변기기를 구입할 때 엘레컴 제품을 선호한다.
제품이 좋아서 그렇기 보다는 개인적인 취향을 반영하는 제품들 중 가장 저렴한 편이기 때문이다.

현재 마우스/키패드/USB 허브를 사용하고 있는데, 이번에 사용하던 소니 이어폰이 사망하셔서 엘레컴 제품으로 구입해봤다.

제품번호는 EHP-IN210(WH) 으로 따로 제품명은 없는 듯 하고...
가격이 가격이다 보니 사용하던 저가형(?) 커널타입 이어폰들과 별차이를 느끼진 못했다...
(1만원 짜리에 기대는 하는넘이 이상하겠지...)

구입기준이 단지

1. 맥북과 어울리게 흰색일 것
2. 커널타입일 것
3. 이어폰을 잘 해먹기 때문에 저렴할 것

이기 때문에...-_-

장점이 있다면... 더 써봐야 알겠지만 이어폰 본체와 실리콘이 사용하던 소니 이어폰에 비해 헐렁(?) 거리지 않아 분실의 위험이 적다는 것...(커널타입 이어폰을 새걸로 바꾸게 되는 가장 큰 이유... 하지만 쓰다보면 헐렁해질지도 모른다.) 그래도 혹시 몰라서 여분으로 들어있는 대형/소형 실리콘을 가방 깊숙한 곳에 넣어 두었다.

단점은 Y형 코드라... 이어폰을 목 뒤로 돌리는 버릇을 가진 나에게는 조금 불편하게 느껴졌다는 것...
그리고 플러그가 굽어지지 않고 1자라 이동시에 파손의 위험이 있다는 것... 정도이다.


인터넷 쇼핑몰 등지에서 1만원정도면 구매 가능하니 이어폰을 막 쓰시는 분들은 참고하시라.
아래는 다나와에 나와있는 제원이다.

기본사양

케이블 길이

1.2m

자체 볼륨 조절

미지원

색상

화이트,레드,블랙

무게

5g(코드 제외)

세부사양

유닛 크기

10mm

임피던스

16Ω

감도

97dB/1mW

주파수 응답

20Hz ~ 20kHz

최대 입력

60mW(JEITA 규격)

기타

밀폐형

고정 실리콘 고무(S,M,L 3 사이즈 부속)

단자

커넥터

3.5mm 크롬 도금 스테레오 미니 플러그


2009년 11월 19일 목요일

MacBook MC240KH/A

얼마전에 구매한 맥북... 투여된 프로젝트가 막장까지 간 것 불끄는 프로젝트라... 산지 꽤 됐는데 이제야 자랑을 한다. 하지만... 처음 사는 맥북이라 알아보지도 않고 어리부리 구입하여 새 맥북이 나오기 몇일 전에 구형을 질러버렸다...-_-

맥북상자

역시 패키징에도 많은 신경을 썼다.


바디

역시 벌레먹은 사과가...


오픈

뚜껑 연 모습


자판

부족하면서도 불편해 보이는 자판... 디자인을 위해서라면...


아는 사람이 얘기하길... 모델이 자주 바뀐다니깐... CPU 좀 빠르고 이런저런 것들 좀 차이가 나는것에 대해서는 별 불만이 없는데...

조금 아쉬운점: 모니터가 LED라는 것...
많이 아쉬운점: 배터리가 7시간 간다는 것...

어차피 개발용이니... 18개월에서 24개월 사이에 교체할테고 30만원이나 싸게 샀으니 그냥 만족하면서 쓰는 수 밖에...

일단 노트북 교체할 시기도 됐고 곧 출시할 아이폰 어플리케이션을 개발하기 위해 구매를 한 건데... 이클립스도 깔 수 있으니 안드로이드 개발도 할 수 있어 그냥 그대로 OSX를 쓰고 싶지만...(XCode 좋더라... 이클립스야 원래 내 사랑이고...)

윈도우즈 모바일 개발 덕분에 VirtualBox를 깔아 윈도우도 같이 돌리고 있다.(그지 같은 VS2008, MS야 돈 아까우니 Express 버전에 모바일 개발환경 넣어다오...-_-) 이넘 땜에 4G로 업그레이드 해야할 듯... 일단 그럭저럭 부족함 없이 돌릴 수 있어서 시간되고 여유될때 업그레이드 하기로...