6.PaintShop 같이 화면 캡처하기

본장에서는 PaintShop 프로그램 과 같이 객체,영역,윈도우,전체화면으로 화면을 캡처하는 방법에 대해서 설명합니다. 본장의 내용은 팁이라기 보다 지금까지 설명한 모든 방법을 응용하는 편에 속합니다. 본책의 지금까지 설명한 내용을 이용하면 화면 캡처 프로그램을 만드실수가 있습니다. 중요 부분은 메시지 훅킹이지만 메시지 훅킹과 함께 비트맵을 이용하는 방법과 영역설정을 위해서 추적 박스를 설정하는 방법과 사용자 메시지 전송과 처리등의 방법들이 이용됩니다. Part1의 3장에서 설명한 화면캡처와 본장에서 소개하는 화면캡처는 기능이 비슷하나 본장에서 구현하는 방법이 완성된 형태입니다.

대상자: 고급

Chapter 등급: 선택

공부방법:

본항목은 화면캡처를 꼭해야 할경우에만 읽어보시기 바랍니다. 지금까지 설명한 모든 내용을 응용한것이기 때문에 중요한 팁이 없습니다.

1.화면 캡처 예제 ExCaputer

필자에게 또는 지킴이에게 질문이 오는 부분중에서 고급 부분에 하나인 것이 메시지 훅킹부분입니다. 훅킹 부분에 대해서 많은 질문은 없었으나 크게 두개로 요약한다면 PaintShop처럼 화면을 캡처하는 방법과 아래한글의 영한사전처럼 스크린에 출력된 문자열을 얻는 방법입니다. 그때 □6.x 책을 쓸때는 둘중에 하나의 예제를 넣어야 겠구나□ 하는 생각을 했었습니다. 그래서 본장에서 PaintShop 예제를 설정하였습니다. □화면 캡처보다 아래한글의 영한사전 처럼 스크린에 출력된 문자열을 얻는 방법이 더 필요할텐데□ 라는 후배의 의견을 무시하고 PaintShop 예제를 선택한것은 아래한글은 국내 프로그램이고 PaintShop은 그렇지 않기 때문이라고 말하고 싶습니다.(그렇다고 뭐 필자가 애국자적인 맘을 가진사람은 아닙니다.) 문자열을 캡처하거나 그래픽을 캡처하거나 중요한것은 메시지 훅킹입니다. 그이외의 내용은 응용입니다. 문자열 예제를 꼭 넣어달라는 지킴이 회원님이 있었는데 그 예제를 쓰지 않은 것에 대해서 이란을 이용하여 죄송하다고 말씀드리고 싶습니다.

먼저 ExCapture 프로그램의 전체 구성에 대해서 설명하겠습니다. 그림 2-6-1은 ExCapture의 전체 구성도 입니다.

(그림 파일)

(그림 2-6-1)ExCapture전체 구성도

그림 2-6-1은 ExCapture 프로그램 실행 형태입니다. ExCapture에는 □캡처□ 라는 메뉴 항목이 있으며 이항목에는 그림 2-6-1의 ⓐ항목처럼 4개의 메뉴가 있습니다.

이 4개의 메뉴에서 □객체□ 항목을 클릭했을때가 그림 2-6-1의 ⓑ항목입니다. □객체□항목을 클릭하면 ExCapture프로그램이 아이콘화 됩니다. 이런상태에서 우측마우스 버튼을 클릭하게 되면 마우스가 위치한 최상위 윈도우 영역에 박스가 설정됩니다. 마우스를 이동시키면 마우스가 위치한 해당 윈도우 영역에 또다시 박스가 설정됩니다. 이런상태에서 좌측마우스를 클릭하면 박스가 설정된 영역이 캡처되고 ⓑ항목의 ③화면처럼 ExCapture가 활성화 됩니다.

□윈도우□ 메뉴를 클릭하면 그림 2-6-1의 ⓒ항목의  ① 항목처럼 □아이콘화□ 됩니다. 이렇게 아이콘화 된 상태에서 캡처하고자 하는 윈도우에서 우측마우스 버튼을 클기하면 해당 윈도우가 캡처됩니다. □객체□ 메뉴와 □윈도우□ 메뉴와 차이점은 □객체□ 는 차일드 윈도우 영역을 캡처하고 □윈도우□ 항목은 마우스가 위치한 프로그램의 전체 윈도우를 캡처하는 것입니다.

□전체화면□ 메뉴를 클릭하면 그림 2-6-1의 ⓓ항목의  ① 항목처럼 □아이콘화□ 됩니다. 이때 우측 마우스 버튼을 클릭하면 스크린 영역이 모두 캡처되고 ③항목처럼 ExCapture가 캡처됩니다.

□영역□ 메뉴 를 선택하게 되면 그림 2-6-1의 ⓔ 항목의  ①항목처럼 □아이콘화□ 됩니다. 이때 우측마우스 버튼을 클릭하면 스크린 영역에 추적박스가 형성됩니다. 이상태가 그림 2-6-1의 ⓔ항목의  ② 항목입니다. 이때 마우스를 이용하여 영역을 이동시키고 크기를 조절한 후에 우측 마우스 버튼을 클릭하거나 Enter키를 클릭하게 되면 그림 2-6-1의 ③ 항목처럼 설정한 영역을 캡처하게 됩니다. 이부분은 PaintShop 의 기능과는 조금 다른 형태입니다. (필자가 원하는 영역 캡처 형태입니다.)

지금까지 설명한내용과 그림 2-6-1이 총 4가지 방법으로 화면을 캡처하는 ExCapture 예제 설명입니다.

2.화면 캡처 로직

ExCapture의 로직은 어떤 형태일까? 본항목에서는 이문제의 해답을 풀고자 합니다. 윈도우 프로그래밍에 어느정도 자신이 있는 분들은 어떤 문제에 부딪혔을때 몇가지 힌트만 주면 바로 해결을 하고 프로그래밍을 할수 있습니다. 자신이 윈도우 프로그래밍에 어느정도 자신이 있는 분들이라면 본항목에서 설명하는 로직만 보시고 다음 항목을 생략하고 직접 프로그램을 해보시기 바랍니다. 또는 지금까지 본책을 보신 분들이라면 본항목만 읽어보시고 직접 프로그램을 해보시기 바랍니다. 지킴이의 묻고 답하기 란에서 질문이 오는것을 볼때 어떤 질문들은 □조금만 생각해 보면 좋았을것을□ 하는 아쉬움이 있었습니다. 이런 아쉬움이 든 이유는  어떤 문제에 부딛혔을때 스스로 해결하고자 하는 마음보다 그냥 빨리 쉽게 해답을 얻고자하는 경향이 매우 많다는 것을 느꼈기 때문입니다. □이런 방법을 사용하세요□ 라고 하면 □어떻게 예제를 얻을수 없습니까?□ 라고 되묻는 경우가 있는데, 이럴경우 무척 아쉬움이 더합니다. 이런 내용을 쓰면 뭐라고 할지 모르지만 영원히 답을 얻어서 또는 예제가 있는 프로그램을 한다면 발전이 없을것입니다. 힌트만 얻어서 정답을 얻기 위해서 여러 방법을 시도했을때 얻을수 있는것입니다.( 이렇게 이야기 하니까 필자가 강력하게 외쳤던 MFC에 노우하우가 없다는 것과 충돌되는 이야기가 되는 군요)

그림 2-6-1과 같은 캡처 프로그램을 만들기 위해서는 두개의 프로그램이 필요합니다. 첫번째는 마우스 메시지를 가로채는 훅킹 DLL이며 두번째는 이 훅킹 DLL를 이용하여 영역을 얻고 이영역을 캡처하는 메인 프로그램입니다. 4개의 각 항목에 따라 프로그래맹 하는 방법은 다음과 같습니다.

<1>객체를 캡처하기

객체를 캡처하는 방법은 다음과 같습니다.

①WH_MOUSE 를 이용하여 마우스 메시지를 캡처한다.

②우측 마우스 버튼을 클릭하면 캡처 플래그를 1로 설정한다.

③캡처 플래그가 설정되면 마우스가 위치한 영역의 윈도우 핸들을 얻고 윈도우 영역을 얻은다음 해당 윈도우에 XOR 모드로 박스를 그린다.

④좌측 마우스 버튼을 클릭하면 스크린 영역에서 해당 영역만 비트맵으로 설정한다.

①항목의 WH_MOUSE를 이용한다는 의미는 SetWindowsHookEx함수를 수행할때 첫번째 인자를 WH_MOSUE를 사용하라는 의미입니다. 이렇게 한후에 필터 함수에서 wParam이 WM_RBUTTONDOWN일경우 플러그를 하나 설정하고 이플러그가 를 1로 설정합니다.  이때부터 마우스가 이동될경우 해당 마우스 위치를 얻습니다.이때는 GetCursorPos 함수를 사용하여 현재 마우스 위치를 POINT 형으로 얻은다음 WindowFromPoint 함수를 이용하여 HWND 핸들을 얻고, GetWindowRect 함수를 이용하여 영역을 얻는것입니다. 이렇게 영역을 얻은다음 영역에 박스를 그려줍니다. 마우스가 해당 영역 밖의 다른 윈도우로 갔을경우에 이전에 그려준 영역을 지우기 위해서 박스를 그릴때 XOR모드로 그려줍니다. 이렇게 하면 ③항목까지가 끝나게 됩니다. 이렇게 한후 좌측마우스 버튼을 클릭하면 현재 플러그가 1로 설정되었을경우 윈도우 영역을 캡처하면 됩니다.

<2>윈도우를 캡처하기

윈도우를 캡처하고자 할경우의 방법은 다음과 같습니다.

①WH_MOUSE 를 이용하여 마우스 메시지를 캡처한다.

②우측 마우스 버튼을 클릭하면 현재 마우스가 위치한 윈도우 핸들을 얻는다.

③현재 윈도우의 부모 윈도우를 얻는다. 이부모 윈도우가 NULL일경우에는 마우스가 위치한 영역위에 있는 프로그램의 전체 윈도우가 된다.

④해당 윈도우를 캡처한다.

①항목은 앞의 객체를 캡처하는 내용과 같습니다. 이후에 우측마우스 버튼을 클릭하면 현재 마우스가 위치한 윈도우 핸들을 얻습니다. 이렇게 윈도우 핸들을 얻으면 이것이 차일드 윈도우 핸들일수도 있습니다. □윈도우□ 메뉴 항목에서는 현재 마우스가 위치한 프로그램의 전체를 캡처하는 것이기 때문에,현재윈도우에서 부모 윈도우로 계속적으로 올라갑니다. 이때 사용하는 함수가 GetParent입니다. 이함수는 부모 윈도우가 없을 경우 NULL를 리턴합니다. 즉 현재 프로그램의 최외곽 윈도우라는 의미입니다. 이때 이윈도우의 영역을 얻고 스크린에서 해당 영역만 비트맵으로 전환하는 것입니다.

<3>전체 화면 캡처하기

전체 화면을 캡처하는 방법은 다음과 같습니다.

①WH_MOUSE 를 이용하여 마우스 메시지를 캡처한다.

②우측 마우스 버튼을 클릭하면 스크린 전체 영역을 캡처한다.

전체 하면을 캡처하는 방법은 매우 간단합니다. 마우스를 캡처하다가 우측마우스 버튼을 클릭하면 그냥 화면 전체를 캡처하면 됩니다. 스크린 전체를 캡처하는 방법은 PART1 의 3장에서 이미 설명한 내용입니다.

<4>영역 캡처하기

영역 캡처하는 방법은 다음과 같습니다.

①WH_MOUSE 를 이용하여 마우스 메시지를 캡처한다.

②우측 마우스 버튼을 클릭하면 외곽 경계선이 없는 스크린 크기만한 대화상자를 만들고 스크린 전체영역을 비트맵으로 캡처한다.

③대화상자에 스크린 전체영역을 캡처한 비트맵을 출력한다.

④대화상자를 최상단 윈도우로 설정한후에 추적 박스를 출력한다.

⑤Enter키를 누르거나 우측 마우스 버튼을 버튼을 클릭하면 추적박스를 감추고 대화상자 윈도우에서 추적박스가 설정된 영역만 캡처한다.

영역을 캡처하는 방법은 약간의 트릭을 사용합니다. 마우스 캡처를 하다가 우측마우스 버튼을 클릭하게 되면 대화상자를 하나 만듭니다. 이대화상자는 경계선이 없는 상태로 만들고 MoveWindow를 이용하여 스크린 크기와 같게 만듭니다. 이렇게 만든후에 스크린 화면을 비트맵으로 캡처한후에 대화상자를 화면에 보이고 이화면에 스크린을 출력합니다. 이렇게 한후에 추적박스를 출력합니다. 이때 사용하는 추적박스는 PART1의 3장에서 제작한 추적박스입니다. 이렇게 되면 약간 화면이 깜빡임이 있을수 있으나 사용자는 전체 하면 영역에 추적박스가 설정된것으로 느끼게 됩니다. 이상태에서 추적박스를 이동시키거나 크기를 조절하게 되면 꼭 스크린에 추적 박스가 설정되고 스크린 영역에서 추적박스를 조절하는 것처럼 느끼게 됩니다. 대화상자를 출력할때 전장에서 설명한 방법으로 대화상자를 최상위 윈도우로 설정합니다. 이렇게 하는 이유는 캡처 프로그램이 아이콘화 되어 있기 때문에 대화상자가 출력되면 최상단이 되지 않기 때문입니다. 전체 화면을 가진 대화상자에서 Enter키를 클릭하거나 우측 마우스 버튼을 클릭하게 되면 대화상자 화면에서 해당영역만 비트맵으로 설정하고 대화상자를 종료 하면 됩니다.

지금까지 설명한 방법을 읽어보고 □아하 어떻게 하는지 알겠다□ 하시는 분들은  다음항목을 읽지 마시고 프로그램을 해주시기 바랍니다.

3.CaptureDll 과 ExCapture 코딩 내용

ExCapture 예제는 마우스 메시지 훅킹을 위해서 CaptureDll이라는 마우스 메시지

훅킹 DLL를 사용합니다. 본항목에서는 전항목에서 사용한 로직을 프로그램으로 구현해 보겠습니다. 이구현 방법은 다음과 같습니다.

<1>객체 캡처

객체를 캡처할때 CaptureDll 의 마우스 메시지 필터 함수는 다음과 같습니다.

//객체를 캡처할경우

LRESULT CALLBACK ObjectCapture (int nCode, WPARAM wParam, LPARAM lParam )

{

LPMOUSEHOOKSTRUCT MouseHookParam;

HWND hWnd;

CRect srect;

CPen cPen,*oldpen;

CDC ScreenDC;

POINT point;

if ( nCode >= 0 ) {

//brBut가 TRUE이면 현재 우측 마우스 버튼이 클릭됨

if(brBut)

{

//좌측 마우스 버튼이 클릭 되면

if(wParam==WM_LBUTTONDOWN || wParam==WM_LBUTTONUP)

{

//화면에서 이전에 그렸던 박스를 지운다.

ScreenDC.CreateDC("DISPLAY", NULL, NULL, NULL);

cPen.CreatePen(PS_SOLID,2,RGB(127,127,127));

ScreenDC.SelectStockObject(NULL_BRUSH);

oldpen=ScreenDC.SelectObject(&cPen);

ScreenDC.SetROP2(R2_XORPEN);

ScreenDC.Rectangle(oldrect);

ScreenDC.SelectObject(oldpen);

//메인 프로그램에 캡처 종료를 알린다.

SendMessage(hwndMain,WM_ENDCAPTURE,0,0);

return TRUE;

}

}

//우측 마우스 버튼을 클릭하면

if(wParam==WM_RBUTTONDOWN || wParam==WM_RBUTTONUP)

{

// brBut를 TRUE로 설정한다.

brBut=TRUE;

//우측 마우스 메시지를 큐에서 제거하기 위해서

//CallNextHookEx 함수를 호출 하지 않는다.

return TRUE;

}

MouseHookParam = (MOUSEHOOKSTRUCT *) lParam;

//현재 마우스 위치를 얻는다.

GetCursorPos(&point);

//마우스가 위치한 윈도우 핸들을 얻는다.

hWnd=WindowFromPoint(point);

//윈도우 영역을 얻는다.

GetWindowRect(hWnd,srect);

//이전의 영역과 같지 않고 우측 마우스 버튼이 눌려지지 않은

//상태이면

if(srect!=oldrect && brBut)

{

ScreenDC.CreateDC("DISPLAY", NULL, NULL, NULL);

cPen.CreatePen(PS_SOLID,2,RGB(127,127,127));

ScreenDC.SelectStockObject(NULL_BRUSH);

oldpen=ScreenDC.SelectObject(&cPen);

ScreenDC.SetROP2(R2_XORPEN);

//이전 에 그렸던 박스 지우고

ScreenDC.Rectangle(oldrect);

//현재 윈도우에 박스를 그린다.

ScreenDC.Rectangle(srect);

ScreenDC.SelectObject(oldpen);

oldrect=srect;

}

}

//우측 마우스 버튼이 클릭되었을때는 모든 마우스 이벤트를

//메시지 큐에서 제거하기 위해서 CallnextHookEx함수를 호출하지 않는다.

if(brBut)

return TRUE;

return( CallNextHookEx(hHook, nCode, wParam, lParam));

}

ObjectCapture 필터 함수는 우측 마우스 버튼을 클릭하면 마우스가 위치한 영역에 박스를 그리다가 다시 좌측마우스 버튼을 클릭하게 되면 메인 윈도우로 WM_ENDCAPTURE 를 보냅니다.  WM_ENDCAPTRUE는 사용자 메시지입니다. 이메시지는 다음과 같이 정의하였습니다.

#define WM_ENDCAPTURE WM_USER+5

메인 윈도우에	WM_ENDCAPTRUE를 보내게 되면 메인윈도우에서는 이메시지에 의해서 수행되는 함수를 만들고 이함수에서 다음과 같이 코딩을 합니다.

ⓐ 화면 캡처하기

CDC memDC;

CDC ScreenDC;

①m_rect=((CMainFrame *)AfxGetMainWnd())->m_rectCapture;

m_pBitmap=new CBitmap;

ScreenDC.CreateDC("DISPLAY", NULL, NULL, NULL);

//스크린 DC와 호환되는 DC 를 만든다

memDC.CreateCompatibleDC(&ScreenDC);

//스크린 DC와 호환도니느 비트맵을 만든다.

m_pBitmap->CreateCompatibleBitmap(&ScreenDC,m_rect.Width(),m_rect.Height());

//비트맵을 메모리 DC에 설정한다.

memDC.SelectObject(m_pBitmap);

//스크린 DC의 화면을 메모리 DC에 설정한다.

memDC.BitBlt(0,0,m_rect.Width(),m_rect.Height(),

&ScreenDC,

m_rect.TopLeft().x,m_rect.TopLeft().y,SRCCOPY) ;

위의 소스를 보면 화면을 캡처하기 위해서 스크린 영역의 DC를 얻은다음 캡처하고자하는 영역의 크기만큼 비트맵을 만들고 이비트맵을 DC로 설정하고 BitBlt를 이용하여 화면에서 특정 영역을 비트맵으로 설정한것입니다.

ExCapture는 MDI형태로 만들어져 있으며 캡처를 하면 캡처된 영역이 을 출력하는 자식윈도우를 하나 만듭니다. 즉 OpenDocument를 이용하여 새로운 템플레이트를 하나 만든다는 것입니다. 따라서 CaptureDll에서 WM_ENDCAPTRUE 메시지를 보내면 메인 프레임에서 이메시지를 받고 영역을 설정하고 새로운 템플레이트를 만들어야 합니다. 새로운 템플레이트가 만들어지면 템플레이트의 도큐먼트에서는 스크린 DC에서 해당 영역을 캡처하고 이것을 도큐먼트의 CBitmap* m_pBitmap 설정한것입니다. 위의 소스에서 ①항목에서 메인프레임의 m_rectCaptrue 를 얻은 이유가 바로 이이유입니다. 위의 캡처 소스가 실행되기 이전에 메인프레임에서는 다음과 같은 항목이 실행되어야 합니다.

GetCursorPos(&point);

hWnd=::WindowFromPoint(point);

::GetWindowRect(hWnd,m_rectCapture);

m_bCapImage=TRUE;

ⓑ새로운 템플레이트 만들기

POSITION pos=AfxGetApp()->GetFirstDocTemplatePosition();

CMultiDocTemplate *pTemplate;

pTemplate=(CMultiDocTemplate *)AfxGetApp()->GetNextDocTemplate(pos);

pTemplate->OpenDocumentFile(NULL);

m_bCapImage=FALSE;

위의 내용은 메인프레임에서 WM_ENDCAPTURE메시지를 받고 수행되는 내용입니다. GetCursorPos함수에 의해서 현재 마우스 위치를 얻은다음 WindowFrmPoint함수를 이용하여 현재 마우스 커서가 위치한 윈도우 핸들을 얻은다음 이 윈도우의 영역을 m_rectCapture에 넣고 새로운 템플레이트를 만들고 OpenDocumentFile를 수행한 형태입니다. 이상태에서 도큐먼트의 OnNewDocument에서 ⓐ내용의 화면캡처를 하는 것입니다. 위와 같이 하면 객체를 캡처할수 있습니다.

<2>윈도우  캡처

윈도우 캡처는 WH_MOUSE를 이용하여 마우스 메시지를 훅킹하다가 우측 버튼을 클릭하면 아무런 작업없이 메인 윈도우에 WM-ENDCAPTURE를 보냅니다. 이내용은 CaptureDll의 OtherCapture 이면 코딩 내용은 다음과 같습니다.

LRESULT CALLBACK OtherCapture (int nCode, WPARAM wParam, LPARAM lParam )

{

if ( nCode >= 0 ) {

if(wParam==WM_RBUTTONDOWN || wParam==WM_RBUTTONUP)

{

SendMessage(hwndMain,WM_ENDCAPTURE,0,0);

return TRUE;

}

}

return( CallNextHookEx(hHook, nCode, wParam, lParam));

}

단순하게 우측 마우스 버튼을 클릭하면 CallNextHookEx함수를 수행하지 않고 메인 프로그램에 WM_ENDCAPTURE를 보냅니다.

이렇게 되면 메인프레임에서는 WM_ENDCAPTURE 메시지를 받고 다음과 같이 수행하면 됩니다.

//현재 마우스가 위치한 윈도우 핸들을 얻는다.

GetCursorPos(&point);

hWnd=::WindowFromPoint(point);

for(;;)

{

//부모 윈도우를 얻는다.

cWnd=::GetParent(hWnd);

//부모윈도우가 없다면 최외곽 윈도우 이므로

//루프 탈출

if(cWnd==NULL)

break;

hWnd=cWnd;

}

::GetWindowRect(hWnd,m_rectCapture);

m_bCapImage=TRUE;

객체 캡처와 달리 현재 마우스가 위치한 윈도우를 얻고 이윈도우에서 계속적으로 부모 윈도우를 얻습니다. 이렇게 할경우 부모 윈도우가 NULL인 현재 윈도우가 최외곽 윈도우 이므로 이윈도우 영역을 m_rectCapture에 저장하고 전항목의 ⓑ내용 즉 새로운 템플레이트 만들기 항목을 수행하여 템플레이트를 만들고 이렇게 되면 ⓐ내용이 수행되므로 해당 영역을 캡처할수가 있습니다.

<3>전체 화면  캡처

전체 하면 캡처는 매우 간단합니다. 전항목의 OtherCapture 를 훅킹 필터 함수로 설정하고 WM_ENDCAPTURE함수가 발생되면 이때 스크린 전체를 영역으로 설정하면 됩니다. 이내용은 다음과 같습니다.

m_rectCapture=CRect(0,0,GetSystemMetrics(SM_CXMAXTRACK),	GetSystemMetrics(SM_CYMAXTRACK));

m_bCapImage=TRUE;

위와 같이 한후에 앞에서 설정한 ⓑ항목 (새로운 템플레이트 만들기) 을 수행하면 자동적으로 ⓐ항목이 수행되고 전체 화면을 캡처할수가 있습니다.

<4>영역 캡처

영역을 캡처하는 것은 조금 복잡합니다. CaptureDll에서 OtherCapture를 훅킹 필터 함수로 설정하고 WM_ENDCAPTURE함수가 발생되면 대화상자를 만듭니다. 이대화상자의 OnCreate에서 화면 전체 영역을 비트맵을 설정하고 대화상자를 화면크기로 바꾼다음 Part1의 3장에서 만든 추적박스를 정당한 영역에 설정합니다.

이내용은 다음과 같습니다.

int CCaptureDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

if (CDialog::OnCreate(lpCreateStruct) == -1)

return -1;

m_rect=CRect(0,0,GetSystemMetrics(SM_CXMAXTRACK),

GetSystemMetrics(SM_CYMAXTRACK));

//대화상자를 화면 크기로 설정

MoveWindow(m_rect);

CDC memDC;

CDC ScreenDC;

m_pBitmap=new CBitmap;

ScreenDC.CreateDC("DISPLAY", NULL, NULL, NULL);

//스크린 DC와 호환되는 DC 를 만든다

memDC.CreateCompatibleDC(&ScreenDC);

//스크린 DC와 호환도니느 비트맵을 만든다.

m_pBitmap->CreateCompatibleBitmap(&ScreenDC,m_rect.Width(),m_rect.Height());

//비트맵을 메모리 DC에 설정한다.

memDC.SelectObject(m_pBitmap);

//스크린 DC의 화면을 메모리 DC에 설정한다.

memDC.BitBlt(0,0,m_rect.Width(),m_rect.Height(),

&ScreenDC,

m_rect.TopLeft().x,m_rect.TopLeft().y,SRCCOPY) ;

m_rectTrack = CMyTracker(this,2,5);

CRect rect=CRect(m_rect.Width()/4,m_rect.Height()/4,

m_rect.Width()/2,m_rect.Height()/2);

m_rectTrack.SetRect(rect);

m_rectTrack.SetProcess(0);

m_bHide=FALSE;

return 0;

}

CMyTracker의 사용법은 PART1의 3장을 참조하기기 바랍니다. CMyTracker를 이용하여 화면에 영역을 수정할수 있도록 한다음 우측 마우스 버튼을 클릭하거나 Enter키를 클릭하면  추적박스를 지웁니다. 추적박스를 지우는 내용은 다음과 같습니다.

m_bHide=TRUE;

Invalidate(FALSE);

SetTimer(1,500,NULL);

m_bHide를 TRUE로 설정하고 OnPaint함수에서는 m_bHide가 TRUE일경우에는 추적박스를 그리지 않게 하면 됩니다. 이렇게 하고 타이머를 이용하여 0.5초후에 OnTimer를 가동하도록 합니다. 이렇게 하는 이유는 m_bHide 를 TRUE로 설정하고 Invalidate를 호출한후에 바로 화면을 캡처하게 되면 추적박스가 지워지기 전에 캡처가 되기 때문에 추적박스가 캡처됩니다. 어느정도 시간을 주어서 추적박스가 완전히 사라진후에 원래 화면을 캡처하기 위해서입니다. 위와 같이 한후에 OnTimer에서는 추적박스의 영역을 매인 프레임의 m_rectCapture로 설정합니다.

그리고 대화상자를 종료합니다. 이내용은 다음과 같습니다.

void CCaptureDlg::OnTimer(UINT nIDEvent)

{

KillTimer(1);

CMainFrame *pFrame=(CMainFrame *)AfxGetMainWnd();

pFrame->m_rectCapture=m_rect;

pFrame->m_bCapImage=TRUE;

pFrame->m_bCapture=FALSE;

pFrame->CreateCapWin();

EndDialog(IDOK);

//	CDialog::OnTimer(nIDEvent);

}

메인 프레임에서는 대화상자를 실행하고 종료 되면  앞의 ⓑ항목 를 수행합니다. 이렇게 되면 새로운 템플레이트에서는 ⓐ 항목을 수행하여 스크린 영역에서 화면을 캡처하게 됩니다. 이때 스크린 영역은 결국 대화상자 영역이 됩니다. 그러나 대화상자가 스크린과 똑같이 설정되어 있기 때문에 스크린 영역이나 대화상자 영역이 같은 것이기 때문에 스크린 영역을 캡처한것과 같게 됩니다.

<5>프로그램 아이콘화 와 원래 형태로 복구

ExCapture프로그램에서 캡처를 실행시키면 프로그램이 아이콘화 되었다가 캡처가 되면 프로그램이 다시 원래대로 복구 됩니다. 이기능은 매우 간단합니다. ShowWindow함수를 이용하면됩니다. ShowWindow 함수에 인자를 SW_SHOWMINIMIZED 로 설정하면 아이콘화 되며 SW_SHOWMAXMIZED 로 설정하면 최대 화면으로 SW_SHOWNORMAL 이면 보통 화면으로 복구됩니다.

지금까지의 내용으로 ExCapture와 CaptureDll를 제작하는 중요 방법을 설명하였습니다. ExCapture프로그램은 View,ChildFrame,Document,Mainframe 모든부분에서 코딩을 하였기 때문에 전체 소스를 기록할수가 없습니다. 나머지 부분은 제공되는 CD  를 참조하시기 바랍니다.

다음은 CaptureDll의 소스부분입니다.

(프로그램 소스시작)

// CaptureDll.cpp : Defines the initialization routines for the DLL.

//

#include "stdafx.h"

#include "CaptureDll.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

/////////////////////////////////////////////////////////////////////////////

// CCaptureDllApp

BEGIN_MESSAGE_MAP(CCaptureDllApp, CWinApp)

//{{AFX_MSG_MAP(CCaptureDllApp)

// NOTE - the ClassWizard will add and remove mapping macros here.

//    DO NOT EDIT what you see in these blocks of generated code!

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////

// CCaptureDllApp construction

CCaptureDllApp::CCaptureDllApp()

{

// TODO: add construction code here,

// Place all significant initialization in InitInstance

}

/////////////////////////////////////////////////////////////////////////////

// The one and only CCaptureDllApp object

CCaptureDllApp theApp;

#pragma data_seg("SHARDATA")

static HWND	hwndMain = NULL;

static HINSTANCE	hInstance;

static BOOL brBut=FALSE;

static CRect oldrect=CRect(0,0,0,0);

static int capturemode;

#pragma data_seg()

HHOOK hHook;

#define WM_ENDCAPTURE WM_USER+5

int FAR PASCAL InitHooksDll(HWND hMain);

int FAR PASCAL InstallHook(int mode);

int FAR PASCAL UnInstallHook();

LRESULT CALLBACK ObjectCapture (int nCode, WPARAM wParam, LPARAM lParam );

LRESULT CALLBACK OtherCapture (int nCode, WPARAM wParam, LPARAM lParam );

int CALLBACK InitHooksDll(HWND hMain)

{

hwndMain = hMain;

hInstance=theApp.m_hInstance;

brBut=FALSE;

oldrect=CRect(0,0,0,0);

return 1;

}

int CALLBACK InstallHook(int mode)

{

capturemode=mode;

brBut=FALSE;

oldrect=CRect(0,0,0,0);

if(mode==0)

hHook=SetWindowsHookEx(WH_MOUSE, (HOOKPROC)ObjectCapture,hInstance, 0);

else

hHook=SetWindowsHookEx(WH_MOUSE, (HOOKPROC)OtherCapture,hInstance, 0);

return 1;

}

int CALLBACK UnInstallHook()

{

UnhookWindowsHookEx(hHook);

return 1;

}

LRESULT CALLBACK OtherCapture (int nCode, WPARAM wParam, LPARAM lParam )

{

if ( nCode >= 0 ) {

if(wParam==WM_RBUTTONDOWN || wParam==WM_RBUTTONUP)

{

SendMessage(hwndMain,WM_ENDCAPTURE,0,0);

return TRUE;

}

}

return( CallNextHookEx(hHook, nCode, wParam, lParam));

}

//객체를 캡처할경우

LRESULT CALLBACK ObjectCapture (int nCode, WPARAM wParam, LPARAM lParam )

{

LPMOUSEHOOKSTRUCT MouseHookParam;

HWND hWnd;

CRect srect;

CPen cPen,*oldpen;

CDC ScreenDC;

POINT point;

if ( nCode >= 0 ) {

//brBut가 TRUE이면 현재 우측 마우스 버튼이 클릭됨

if(brBut)

{

//좌측 마우스 버튼이 클릭 되면

if(wParam==WM_LBUTTONDOWN || wParam==WM_LBUTTONUP)

{

//화면에서 이전에 그렸던 박스를 지운다.

ScreenDC.CreateDC("DISPLAY", NULL, NULL, NULL);

cPen.CreatePen(PS_SOLID,2,RGB(127,127,127));

ScreenDC.SelectStockObject(NULL_BRUSH);

oldpen=ScreenDC.SelectObject(&cPen);

ScreenDC.SetROP2(R2_XORPEN);

ScreenDC.Rectangle(oldrect);

ScreenDC.SelectObject(oldpen);

//메인 프로그램에 캡처 종료를 알린다.

SendMessage(hwndMain,WM_ENDCAPTURE,0,0);

return TRUE;

}

}

//우측 마우스 버튼을 클릭하면

if(wParam==WM_RBUTTONDOWN || wParam==WM_RBUTTONUP)

{

// brBut를 TRUE로 설정한다.

brBut=TRUE;

//우측 마우스 메시지를 큐에서 제거하기 위해서

//CallNextHookEx 함수를 호출 하지 않는다.

return TRUE;

}

MouseHookParam = (MOUSEHOOKSTRUCT *) lParam;

//현재 마우스 위치를 얻는다.

GetCursorPos(&point);

//마우스가 위치한 윈도우 핸들을 얻는다.

hWnd=WindowFromPoint(point);

//윈도우 영역을 얻는다.

GetWindowRect(hWnd,srect);

//이전의 영역과 같지 않고 우측 마우스 버튼이 눌려지지 않은

//상태이면

if(srect!=oldrect && brBut)

{

ScreenDC.CreateDC("DISPLAY", NULL, NULL, NULL);

cPen.CreatePen(PS_SOLID,2,RGB(127,127,127));

ScreenDC.SelectStockObject(NULL_BRUSH);

oldpen=ScreenDC.SelectObject(&cPen);

ScreenDC.SetROP2(R2_XORPEN);

//이전 에 그렸던 박스 지우고

ScreenDC.Rectangle(oldrect);

//현재 윈도우에 박스를 그린다.

ScreenDC.Rectangle(srect);

ScreenDC.SelectObject(oldpen);

oldrect=srect;

}

}

//우측 마우스 버튼이 클릭되었을때는 모든 마우스 이벤트를

//메시지 큐에서 제거하기 위해서 CallnextHookEx함수를 호출하지 않는다.

if(brBut)

return TRUE;

return( CallNextHookEx(hHook, nCode, wParam, lParam));

}

(프로그램 소스끝)