반응형

http://www.soen.kr/

.더블 버퍼링 활용

화면 깜박임이 발생하는 원인에 대해 연구해 보았고 그 해결책으로 더블 버퍼링이라는 멋진 방법을 소개했다. 그렇다면 더블 버퍼링을 과연 언제 어떻게 사용해야 잘 활용했다고 소문이 날까? 더블 버퍼링의 용도는 꼭 화면 깜박임을 제거하는데만 있는 것은 아니다. 내부 버퍼에서 틈틈이 작업을 할 수 있으므로 아이들(Idle) 시간을 활용하기 위해서 사용할 수도 있고 내부 버퍼를 외부 버퍼보다 더 크게 만들어 스크롤에 활용할 수도 있다.

여기서는 더블 버퍼링의 원리에 대해서만 이해하도록 하고 실무를 할 때 더블 버퍼링을 쓰면 좋겠다는 생각이 들면 적극적으로 활용해 보기 바란다. 다음 예제는 더블 버퍼링을 활용한 갱 화면이다. 갱(Gang) 화면이란 프로그램 제작자를 소개하는 용도를 가지며 일반적으로 숨겨져 있지만 제작자 자신을 표현한다는 면에 있어 다소 멋을 좀 부리는 경향이 있다. 이 예제는 배경 비트맵을 깔고 그 위에서 제작자 목록을 위로 스크롤하는 예를 보여준다.

 

#include "resource.h"

HBITMAP hBit, hBaby;

TCHAR szGang[]="Gang Version 1.0\r\n\r\n 감독 :  정수\r\n"

"개발자 :  상형\r\n사진 모델 :  한슬\r\n협찬 :  미영";

int my;

 

void DrawBitmap(HDC hdc,int x,int y,HBITMAP hBit)

{

HDC MemDC;

HBITMAP OldBitmap;

int bx,by;

BITMAP bit;

 

MemDC=CreateCompatibleDC(hdc);

OldBitmap=(HBITMAP)SelectObject(MemDC, hBit);

 

GetObject(hBit,sizeof(BITMAP),&bit);

bx=bit.bmWidth;

by=bit.bmHeight;

 

BitBlt(hdc,0,0,bx,by,MemDC,0,0,SRCCOPY);

 

SelectObject(MemDC,OldBitmap);

DeleteDC(MemDC);

}

 

void OnTimer()

{

RECT crt;

HDC hdc,hMemDC;

HBITMAP OldBit;

HFONT font, oldfont;

RECT grt;

int i,j;

 

GetClientRect(hWndMain,&crt);

hdc=GetDC(hWndMain);

 

if (hBit==NULL) {

    hBit=CreateCompatibleBitmap(hdc,crt.right,crt.bottom);

}

hMemDC=CreateCompatibleDC(hdc);

OldBit=(HBITMAP)SelectObject(hMemDC,hBit);

 

DrawBitmap(hMemDC,0,0,hBaby);

SetBkMode(hMemDC,TRANSPARENT);

 

font=CreateFont(30,0,0,0,0,0,0,0,HANGEUL_CHARSET,3,2,1,

    VARIABLE_PITCH | FF_ROMAN,"궁서");

oldfont=(HFONT)SelectObject(hMemDC,font);

 

my--;

if (my==20) {

    KillTimer(hWndMain,1);

}

 

SetTextColor(hMemDC,RGB(192,192,192));

for (i=-1;i<=1;i++) {

    for (j=-1;j<=1;j++) {

       SetRect(&grt,10+i,my+j,400+i,my+300+j);

       DrawText(hMemDC,szGang,-1,&grt,DT_WORDBREAK);

    }

}

 

SetTextColor(hMemDC,RGB(32,32,32));

SetRect(&grt,10,my,400,my+300);

DrawText(hMemDC,szGang,-1,&grt,DT_WORDBREAK);

 

SelectObject(hMemDC,oldfont);

DeleteObject(font);

 

SelectObject(hMemDC,OldBit);

DeleteDC(hMemDC);

ReleaseDC(hWndMain,hdc);

InvalidateRect(hWndMain,NULL,FALSE);

}

 

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

HDC hdc,hMemDC;

PAINTSTRUCT ps;

HBITMAP OldBit;

RECT crt;

 

switch(iMessage) {

case WM_CREATE:

    hBaby=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_BITMAP1));

case WM_LBUTTONDOWN:

    SetTimer(hWnd,1,70,NULL);

    my=310;

    return 0;

case WM_TIMER:

    OnTimer();

    return 0;

case WM_PAINT:

    hdc=BeginPaint(hWnd, &ps);

    GetClientRect(hWnd,&crt);

    hMemDC=CreateCompatibleDC(hdc);

    OldBit=(HBITMAP)SelectObject(hMemDC, hBit);

    BitBlt(hdc,0,0,crt.right,crt.bottom,hMemDC,0,0,SRCCOPY);

    SelectObject(hMemDC, OldBit);

    DeleteDC(hMemDC);

    EndPaint(hWnd, &ps);

    return 0;

case WM_DESTROY:

    if (hBit) {

       DeleteObject(hBit);

    }

    DeleteObject(hBaby);

    PostQuitMessage(0);

    KillTimer(hWnd,1);

    return 0;

}

return(DefWindowProc(hWnd,iMessage,wParam,lParam));

}

 

실행 결과는 다음과 같다. 움직이는 화면이므로 직접 실행해 봐야 결과를 볼 수 있다. 예쁜 아기 그림이 있고 아래에서 문자열이 천천히 위로 올라오는 동작을 한다.

  

문자열은 바깥쪽에 회색 테두리를 가지도록 했으며 보기 편하도록 큼직한 폰트를 사용했다. 비트맵 위에서 글자가 움직이지만 깜박임은 전혀 없으며 아주 부드럽게 스크롤되는 것을 볼 수 있다. 만약 이런 화면을 더블 버퍼링으로 처리하지 않는다면 배경 비트맵과 그림이 계속 반복적으로 화면에 나타나기 때문에 깜박임이 심해지고 갱 화면으로서 가치가 떨어질 것이다.

좀 더 코드를 작성한다면 글자들이 오른쪽에서 왼쪽으로 한 줄씩 날라 오도록 할 수도 있고 점점 확대되는 모양으로 만들 수도 있다. 또는 약간의 애니메이션을 첨가한다거나 글자의 색상을 조작하여 Fade In, Fade Out 등의 장면 전환 효과를 낼 수도 있다. 아뭏든 더블 버퍼링을 쓰기만 하면 어떠한 모양도 깔끔하게 화면으로 구현할 수 있으므로 기발한 상상력을 발휘해 볼만하다.

반응형
반응형

http://www.soen.kr/


.더블 버퍼링

화면 출력의 깜박임을 해결할 수 있는 근본적이고도 완전한 방법은 더블 버퍼링(Double Buffering)뿐이다. 더블 버퍼링이란 용어 그대로 버퍼를 두 개(또는 그 이상) 사용하는 방식인데 화면에 보여줄 버퍼와 내부 계산에 사용할 버퍼를 따로 유지한다. 내부 버퍼에 미리 그림을 그린 후 화면 버퍼로 고속 전송하며 그리는 중간 과정을 숨겨진 내부 버퍼에서 처리함으로써 사용자는 최종 결과만 보도록 하는 기법이다.

내부 버퍼에서 일어나는 일은 사용자에게 보이지 않기 때문에 그림이 아무리 복잡해도, 화면을 다 지운 후 다시 그리더라도 깜박임을 전혀 목격할 수가 없다. 뿐만 아니라 그리는 순서에 따라 이미지간의 수직적인 아래 위를 지정할 수 있으며 여러 개의 이미지를 동시에 움직이는 것도 아주 부드럽게 처리할 수 있다.

더블 버퍼링에 사용되는 내부 버퍼는 구체적으로 메모리 영역인데 이 메모리 영역은 외부 버퍼, 즉 화면의 포맷과 호환되어야 한다. 그래야 내부 버퍼에 그린 그림을 별도의 조작없이 외부 버퍼로 고속 전송할 수 있다. 과거 DOS 시절에는 내부 버퍼를 비디오 램의 물리적인 포맷대로 작성한 후 비디오 램으로 곧바로 전송하는 방식을 사용했었다. 또는 아예 비디오 카드가 하드웨어적으로 여러 개의 페이지를 제공하여 페이지를 교체하는 방식을 사용하기도 했다.

윈도우즈에서는 내부 버퍼를 메모리에 직접 작성할 필요가 없는데 왜냐하면 비트맵이 내부 버퍼 역할을 해 주기 때문이다. 화면 DC와 호환되는, 즉 색상 포맷이 같고 크기가 동일한 비트맵을 생성한 후 이 비트맵에 그림을 그리면 비트맵 자체가 내부 버퍼 역할을 하게 된다. 비트맵에 그려진 그림을 화면으로 전송할 때는 물론 BitBlt 함수를 사용한다. 앞에서 만든 Bounce 예제를 더블 버퍼링으로 다시 작성해 보자.

 

#define R 20

int x,y;

int xi,yi;

HBITMAP hBit;

void OnTimer()

{

RECT crt;

HDC hdc,hMemDC;

HBITMAP OldBit;

HPEN hPen,OldPen;

HBRUSH hBrush,OldBrush;

int i;

 

GetClientRect(hWndMain,&crt);

hdc=GetDC(hWndMain);

 

if (hBit==NULL) {

    hBit=CreateCompatibleBitmap(hdc,crt.right,crt.bottom);

}

hMemDC=CreateCompatibleDC(hdc);

OldBit=(HBITMAP)SelectObject(hMemDC,hBit);

 

FillRect(hMemDC,&crt,GetSysColorBrush(COLOR_WINDOW));

 

if (x <= R || x >= crt.right-R) {

    xi*=-1;

}

if (y <= R || y >= crt.bottom-R) {

    yi*=-1;

}

x+=xi;

y+=yi;

 

for (i=0;i<crt.right;i+=10) {

    MoveToEx(hMemDC,i,0,NULL);

    LineTo(hMemDC,i,crt.bottom);

}

 

for (i=0;i<crt.bottom;i+=10) {

    MoveToEx(hMemDC,0,i,NULL);

    LineTo(hMemDC,crt.right,i);

}

 

hPen=CreatePen(PS_INSIDEFRAME,5,RGB(255,0,0));

OldPen=(HPEN)SelectObject(hMemDC,hPen);

hBrush=CreateSolidBrush(RGB(0,0,255));

OldBrush=(HBRUSH)SelectObject(hMemDC,hBrush);

Ellipse(hMemDC,x-R,y-R,x+R,y+R);

DeleteObject(SelectObject(hMemDC,OldPen));

DeleteObject(SelectObject(hMemDC,OldBrush));

 

SelectObject(hMemDC,OldBit);

DeleteDC(hMemDC);

ReleaseDC(hWndMain,hdc);

InvalidateRect(hWndMain,NULL,FALSE);

}

 

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

HDC hdc,hMemDC;

PAINTSTRUCT ps;

HBITMAP OldBit;

RECT crt;

 

switch(iMessage) {

case WM_CREATE:

    x=50;

    y=50;

    xi=4;

    yi=5;

    SetTimer(hWnd,1,25,NULL);

    return 0;

case WM_TIMER:

    OnTimer();

   return 0;

case WM_PAINT:

    hdc=BeginPaint(hWnd, &ps);

    GetClientRect(hWnd,&crt);

    hMemDC=CreateCompatibleDC(hdc);

    OldBit=(HBITMAP)SelectObject(hMemDC, hBit);

    BitBlt(hdc,0,0,crt.right,crt.bottom,hMemDC,0,0,SRCCOPY);

    SelectObject(hMemDC, OldBit);

   DeleteDC(hMemDC);

    EndPaint(hWnd, &ps);

    return 0;

case WM_DESTROY:

    if (hBit) {

       DeleteObject(hBit);

    }

    PostQuitMessage(0);

    KillTimer(hWnd,1);

    return 0;

}

return(DefWindowProc(hWnd,iMessage,wParam,lParam));

}

 

전역 비트맵 핸들 hBit가 선언되어 있으며 이 비트맵은 작업영역과 동일한 크기대로 생성된다. OnTimer에서 메모리 DC를 생성하고 이 DC에 비트맵을 선택한 후 메모리 DC에 그림을 출력하면 모든 출력이 비트맵에 작성될 것이다. Bounce 예제의 WM_PAINT에 있던 그리기 코드들이 모두 OnTimer로 이동되었다. OnTimer는 비트맵에 그림을 그린 후 InvalidateRect 함수를 호출하여 작업영역을 무효화하기만 한다. 이때 비트맵으로 화면을 완전히 덮을 수 있으므로 작업 영역은 지울 필요가 없으며 마지막 인수는 FALSE로 주어야 한다.

이 예제에서 OnTimer 함수는 내부 버퍼에 미리 그림을 그려 두는 작업을 하는데 이 함수가 더블 버퍼링의 핵심이다. OnTimer의 직접적인 결과물은 hBit에 그려진 그림뿐이며 이 비트맵에 그림을 그리는 과정은 아무래도 상관없다. 모두 지운 후 그리든, 엎어서 그리든 어차피 사용자에게는 보이지 않는다.

이 코드에서 흔히 오해하기 쉬운 것이 있는데 메모리 비트맵인 hBit와 메모리 DC인 hMemDC와의 관계이다. GDI 출력 함수들은 반드시 DC 핸들을 요구하며 비트맵에 출력하기 위해서는 이 비트맵을 선택하고 있는 메모리 DC의 핸들이 필요하다. 그래서 화면 DC와 호환되는(=비트맵과 호환되는) hMemDC를 생성하고 여기에 비트맵을 선택한 후 출력했다. 이 DC는 어디까지나 비트맵 출력을 위한 임시 DC이므로 비트맵을 다 작성하고 난 다음에는 해제되어야 한다.

더블 버퍼링에서 내부 버퍼라고 칭하는 것은 비트맵이지 메모리 DC가 아니다. 메모리 DC는 비트맵을 선택하기 위해 잠시만 사용되는 DC일 뿐인데 알다시피 비트맵을 선택할 수 있는 DC는 메모리 DC밖에 없기 때문이다. 그래서 전역으로 저장해야 할 대상은 hBit 비트맵이지 hMemDC가 아니다.

WM_PAINT에서는 OnTimer가 작성해 놓은 비트맵을 화면으로 전송하기만 한다. 즉, 이미 그려져 있는 그림(내부 버퍼)을 화면(외부 버퍼)으로 복사만 하는 것이다. 실행 결과는 다음과 같다. 지면으로 보기에는 결과가 동일하지만 실제로 실행해 보면 깜박임을 전혀 느낄 수 없을 것이다. 애니메이션이 아주 부드럽게 실행된다.

이 경우도 OnTimer에서 FillRect 함수를 호출하여 이전 그림을 지우기는 하는데 내부 버퍼에서 일어나는 일이기 때문에 화면을 지운 상태는 사용자 눈에 보이지 않으며 따라서 깜박임이 전혀 없는 것이다. 이런 방식대로라면 여러 개의 공을 한꺼번에 움직이더라도 전혀 무리가 없다. Bounce3 예제는 한꺼번에 10개의 공을 움직인다.

 

#define R 20

int x[10],y[10];

int xi[10],yi[10];

HBITMAP hBit;

void OnTimer()

{

RECT crt;

HDC hdc,hMemDC;

HBITMAP OldBit;

HPEN hPen,OldPen;

HBRUSH hBrush,OldBrush;

int i,ball;

 

GetClientRect(hWndMain,&crt);

hdc=GetDC(hWndMain);

 

if (hBit==NULL) {

    hBit=CreateCompatibleBitmap(hdc,crt.right,crt.bottom);

}

hMemDC=CreateCompatibleDC(hdc);

OldBit=(HBITMAP)SelectObject(hMemDC,hBit);

 

FillRect(hMemDC,&crt,GetSysColorBrush(COLOR_WINDOW));

for (i=0;i<crt.right;i+=10) {

    MoveToEx(hMemDC,i,0,NULL);

    LineTo(hMemDC,i,crt.bottom);

}

 

for (i=0;i<crt.bottom;i+=10) {

    MoveToEx(hMemDC,0,i,NULL);

    LineTo(hMemDC,crt.right,i);

}

 

hPen=CreatePen(PS_INSIDEFRAME,5,RGB(255,0,0));

OldPen=(HPEN)SelectObject(hMemDC,hPen);

hBrush=CreateSolidBrush(RGB(0,0,255));

OldBrush=(HBRUSH)SelectObject(hMemDC,hBrush);

 

for (ball=0;ball<10;ball++) {

    if (x[ball] <= R || x[ball] >= crt.right-R) {

       xi[ball]*=-1;

    }

    if (y[ball] <= R || y[ball] >= crt.bottom-R) {

       yi[ball]*=-1;

    }

    x[ball]+=xi[ball];

    y[ball]+=yi[ball];

 

    Ellipse(hMemDC,x[ball]-R,y[ball]-R,x[ball]+R,y[ball]+R);

}

 

 

DeleteObject(SelectObject(hMemDC,OldPen));

DeleteObject(SelectObject(hMemDC,OldBrush));

 

SelectObject(hMemDC,OldBit);

DeleteDC(hMemDC);

ReleaseDC(hWndMain,hdc);

InvalidateRect(hWndMain,NULL,FALSE);

}

 

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

HDC hdc,hMemDC;

PAINTSTRUCT ps;

HBITMAP OldBit;

RECT crt;

int ball;

 

switch(iMessage) {

case WM_CREATE:

    for (ball=0;ball<10;ball++) {

       x[ball]=50;

       y[ball]=50;

    }

    xi[0]=4;yi[0]=5;

    xi[1]=5;yi[1]=4;

    xi[2]=3;yi[2]=4;

    xi[3]=8;yi[3]=3;

    xi[4]=3;yi[4]=8;

    xi[5]=2;yi[5]=1;

    xi[6]=10;yi[6]=12;

    xi[7]=12;yi[7]=16;

    xi[8]=3;yi[8]=3;

    xi[9]=6;yi[9]=7;

    SetTimer(hWnd,1,25,NULL);

    return 0;

case WM_TIMER:

    OnTimer();

    return 0;

case WM_PAINT:

    hdc=BeginPaint(hWnd, &ps);

    GetClientRect(hWnd,&crt);

    hMemDC=CreateCompatibleDC(hdc);

    OldBit=(HBITMAP)SelectObject(hMemDC, hBit);

    BitBlt(hdc,0,0,crt.right,crt.bottom,hMemDC,0,0,SRCCOPY);

    SelectObject(hMemDC, OldBit);

    DeleteDC(hMemDC);

    EndPaint(hWnd, &ps);

    return 0;

case WM_DESTROY:

    if (hBit) {

       DeleteObject(hBit);

    }

    PostQuitMessage(0);

    KillTimer(hWnd,1);

    return 0;

}

return(DefWindowProc(hWnd,iMessage,wParam,lParam));

}

 

공의 좌표를 기억하는 x,y와 좌표의 증분값인 ix,iy를 크기 10의 배열로 정의했으며 OnTimer에서 10번 루프를 돌면서 공 10개를 한꺼번에 이동시킨다. 각 공의 이동 증분을 다르게 주어 이동 속도를 다양하게 처리해 보았다. 직접 실행해 보면 알겠지만 한꺼번에 10개의 물체가 복잡하게 움직이더라도 전혀 깜박거리지 않으며 속도가 심하게 느려지는 것도 아니다.

더블 버퍼링의 단점이라면 일단 내부 버퍼에 그림을 그린 후 다시 외부 버퍼로 전송하기 때문에 전체적인 속도가 느려질 수 있다는 점이다. 이때 1초에 몇번씩 전송할 수 있는가를 프레임 레이트(Frame Rate)라고 하는데 내부 버퍼에 그림을 준비하는 과정이 복잡할수록 프레임 수가 떨어진다. 다행히 보통 사람의 눈은 1초에 16번 이상의 변화를 감지하지 못하기 때문에 16프레임 이상만 되면 부드러운 움직임을 구현할 수 있다. 물론 그보다 프레임 수가 더 높으면 애니메이션의 품질은 더욱 높아질 것이다.

반응형
반응형

http://www.soen.kr/

깜박임 문제

윈도우에 무엇인가를 반복적으로 출력하려면 깜박임(Flickering)이 발생한다. 특히 반복적으로 그림이 교체되는 애니메이션이나 게임같은 경우 깜박임이 무척 심한데 이런 깜박임은 눈을 피로하게 만들기 때문에 프로그램의 질을 현격하게 떨어뜨린다. 이런 프로그램을 오래 쓰다 보면 심지어 머리까지 아파올 지경이다. 그렇다면 왜 깜박임이 발생하는지 원인을 분석해 보고 그 해결책을 모색해 보도록 하자.

다음 Bounce 예제는 배경 화면에 바둑판 모양의 무늬를 그려 두고 이 무늬 위에서 공을 이동시킨다. 공은 윈도우의 벽에 부딪치면 입사각과 같은 각도로 반사되어 사각의 폐쇄된 공간에서 반복적인 반사 운동을 한다. 게임이든 애니메이션이든 화면에서 무엇인가 움직이는 프로그램이라고 가정하도록 하자. 문제를 정형화하기 위해 메인 윈도우의 크기는 640*350의 고정 크기를 갖도록 했는데 가변 크기라도 해결 방법은 비슷하게 적용할 수 있다.

 

#define R 20

int x,y;

int xi,yi;

void OnTimer()

{

RECT crt;

 

GetClientRect(hWndMain,&crt);

if (x <= R || x >= crt.right-R) {

    xi*=-1;

}

if (y <= R || y >= crt.bottom-R) {

    yi*=-1;

}

x+=xi;

y+=yi;

 

InvalidateRect(hWndMain,NULL,TRUE);

}

 

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

HDC hdc;

PAINTSTRUCT ps;

HPEN hPen,OldPen;

HBRUSH hBrush,OldBrush;

RECT crt;

int i;

 

switch(iMessage) {

case WM_CREATE:

    x=50;

    y=50;

    xi=4;

    yi=5;

    SetTimer(hWnd,1,25,NULL);

    return 0;

case WM_TIMER:

    OnTimer();

    return 0;

case WM_PAINT:

    hdc=BeginPaint(hWnd, &ps);

    GetClientRect(hWnd,&crt);

    for (i=0;i<crt.right;i+=10) {

       MoveToEx(hdc,i,0,NULL);

       LineTo(hdc,i,crt.bottom);

    }

 

    for (i=0;i<crt.bottom;i+=10) {

       MoveToEx(hdc,0,i,NULL);

       LineTo(hdc,crt.right,i);

    }

 

    hPen=CreatePen(PS_INSIDEFRAME,5,RGB(255,0,0));

    OldPen=(HPEN)SelectObject(hdc,hPen);

    hBrush=CreateSolidBrush(RGB(0,0,255));

    OldBrush=(HBRUSH)SelectObject(hdc,hBrush);

    Ellipse(hdc,x-R,y-R,x+R,y+R);

    DeleteObject(SelectObject(hdc,OldPen));

    DeleteObject(SelectObject(hdc,OldBrush));

    EndPaint(hWnd, &ps);

    return 0;

case WM_DESTROY:

    PostQuitMessage(0);

    KillTimer(hWnd,1);

    return 0;

}

return(DefWindowProc(hWnd,iMessage,wParam,lParam));

}

 

코드 자체는 무척 간단하다. 네 개의 전역 변수와 한 개의 매크로 상수가 정의되어 있는데 x,y는 공의 현재 좌표이며 R은 공의 반지름, xi, yi는 각각 공의 수평, 수직 이동 증분이다.  공의 반지름은 20으로 정의되어 있으며 최초 공은 (50,50)에 위치하여 매 0.025초마다 x쪽으로 4픽셀씩 움직이며 y쪽으로 5픽셀씩 움직인다.

WM_CREATE에서 타이머를 설치하고 OnTimer에서 x,y좌표를 xi, yi만큼 증가시키되 벽에 부딪치면 xi, yi의 부호를 바꾸어준다. 즉 아래로 내려가다가 바닥에 닿으면 다시 위로 올라가며 오른쪽 벽에 부딪히면 왼쪽으로 이동한다. OnPaint에서는 바둑판의 격자 무늬를 출력하고 x,y좌표에 공을 출력하였다. 실행 모습은 다음과 같다.

실제로 실행해 보면 공이 움직이기는 하지만 화면 깜박임이 아주 심하게 느껴질 것이며 컴퓨터가 느리면 느릴수록 깜박임이 더욱 심해진다. 컴퓨터가 아주 빠르다면 깜박임의 정도가 덜하겠지만 그렇다고 해서 깜박임이 없어지는 것은 아니며 다만 깜박임의 주파수만 높아질 뿐이다. 그렇다면 왜 이런 깜박임이 생기는 것일까? 그 이유는 InvalidateRect 함수의 마지막 인수에 있는데 이 값이 TRUE이면 화면 전체를 다시 지운 후 그리기 때문이다.

화면을 다시 지우면 WM_ERASEBKGND 메시지가 발생하며 이 메시지에서 윈도우 클래스에 등록된 배경 브러시로 작업영역 전체를 완전히 지운다. 그리고 WM_PAINT에서 깨끗하게 지워진 작업영역에 격자와 공을 다시 그리기 때문에 완전히 지워진 상황과 그림이 그려진 상황이 반복적으로 눈에 보이게 되므로 깜박임이 느껴지는 것이다.

격자가 촘촘히 그려진 화면과 흰색으로 깨끗하게 지워진 화면이 계속 교체되므로 화면이 떨리는 것처럼 느껴진다. 이때 깜박임의 정도는 화면이 지워진 후 얼마나 빨리 다시 그리기를 하는가, 즉 빈 화면인 상태가 얼마나 오래 가는가에 따라 달라진다. 격자 무늬와 공을 최대한 빨리 그리면 깜박임의 정도가 덜해지기는 하지만 그래도 흰 화면이 아예 없어지지 않는 한 깜박임이 없어지지는 않는다.

깜박임의 원인이 화면을 지우는 것 때문이라면 화면을 지우지 않음으로써 깜박임을 없앨 수 있을 것이다. 과연 그런지 InvalidateRect 함수의 마지막 인수를 FALSE로 바꾼 후 테스트해 보자. 또는 WM_ERSEBKGND 메시지를 막아 버리든가 아니면 윈도우 클래스의 배경 브러시를 NULL로 지정해 주어도 된다. 결과는 다음과 같다.

실제로 실행해 보면 과연 화면은 전혀 깜박이지 않는다. 원래 그려져 있던 그림을 지우지 않기 때문에 빈 화면이 눈에 보이지 않으므로 깜박임이 없는 것이다. 그렇지만 지금 이 결과가 원하는 바는 아니다. 공이 움직이기는 하지만 잔상이 지워지지 않기 때문에 원래 그려져 있던 공이 그 자리에 계속 남아있으며 장면 전환이 제대로 되지 않았다.

결국 애니메이션을 제대로 처리하려면 원래 그려져 있던 그림을 지우고 새 그림을 다시 그려야 한다. 그래서 InvalidateRect 함수의 마지막 인수를 TRUE로 해 주거나 아니면 따로 잔상을 지워 주는 코드를 작성해야 한다. 애니메이션을 제대로 하면서 깜박임을 없애려면 여러 가지 방법을 동원할 수 있다.

우선 가장 쉬운 방법은 무효영역을 최소화하여 깜박임을 거의 느낄 수 없도록 만드는 것이다. 위 예제의 경우 움직이는 물체는 공 뿐이고 이 공은 최대 5픽셀 이상 움직이지 않으므로 현재 공의 위치 x,y에서 반지름 R과 최대 이동거리 5만큼의 영역만 무효화시킨다. 그러면 공 주변만 다시 그려지고 나머지는 그대로 있게 되므로 그리는 속도가 빨라지고 깜박임을 거의 느낄 수 없게 된다. 이 방법은 속도가 아주 빠른 장점이 있으며 실제로 거의 깜박임을 느낄 수 없을 정도로 효율이 좋기는 하다. 하지만 애니메이션 영역이 좁아야 한다는 제약이 있어 일반적인 해법이라 할 수 없다.

두번째 방법은 원래 그림을 지우지는 않되 새로 그려지는 그림으로 덮어 쓰는 것이다. 그러면 적어도 빈 화면이 보이지는 않기 때문에 깜박임은 눈에 보이지 않는다. 예를 들어 흰 바탕에 "ABC" 문자열이 출력되어 있는 상황에서 "de"로 교체한다고 해 보자. 이때 이전 문자를 지우지 않고 "de"만 출력하면 "deC"라고 출력될 것이다. 이때 "de" 뒤에 공백을 넣어 "de    "를 출력하면 이전 문자를 완전히 깔끔하게 덮어 버릴 수 있다. 이 방법은 배경과 그려지는 그림이 단순할 때만 적용할 수 있으며 위 예제는 격자가 있기 때문에 적용하기 어렵다.

이외에 깜박임을 최소화할 수 있는 여러 가지 방법들이 있는데 프로그램의 상황에 따라 적용할 수 있는 기법에 제약이 아주 많다. 움직이는 물체가 많거나 서로 겹치지 않도록 해야 한다면 이런 간단한 방법들을 쓰기는 어렵다. 더구나 게임같은 복잡한 프로그램은 물체가 스스로 애니메이션까지 되어야 하므로 보통의 방법으로는 깜박임을 제거하기 어렵다. 깜박임을 없애기 어려운 근본적인 이유는 원래 그림을 지워야만 새로운 그림을 출력할 수 있기 때문이다.

반응형
반응형
http://mnlt.tistory.com/131

ListControl은 MFC에서 굉장히 유용히 쓰이는 컨트롤입니다.
해당 컨트롤에서 직접 원하는 위치를 즉석 수정하기 위한 컨트롤을 만들었습니다.

아마 그냥 유용하게 쓰일듯 싶기도 해서 포스팅.

EditListCtrl.h
  1. #pragma once  
  2.   
  3.   
  4. // CEditListCtrl  
  5.   
  6. class CEditListCtrl : public CListCtrl  
  7. {  
  8.     DECLARE_DYNAMIC(CEditListCtrl)  
  9.   
  10. public:  
  11.     CEditListCtrl();  
  12.     virtual ~CEditListCtrl();  
  13.   
  14. protected:  
  15.     DECLARE_MESSAGE_MAP()  
  16.   
  17. public:  
  18.     virtual BOOL PreTranslateMessage(MSG* pMsg);  
  19.     afx_msg void OnNMClick(NMHDR *pNMHDR, LRESULT *pResult);  
  20.   
  21.     // 초기화 함수.  
  22.     void Init(void);  
  23.   
  24.     // CEditListCtrl 을 사용하는 Dialog 의 OnOK 함수가 호출될때 호출해야 한다.  
  25.     void OnOK(void);  
  26.   
  27.     // List 값을 바꾼다.  
  28.     BOOL SetText(CString _val, int _row, int _col);  
  29.   
  30. protected:  
  31.     // ListCtrl 을 클릭했을때 nItem 저장될 변수. (Row)  
  32.     int m_nItem;  
  33.   
  34.     // ListCtrl 을 클릭했을때 nSubItem 저장될 변수 (Col);  
  35.     int m_nSubItem;  
  36.   
  37.     // 현재 포커스가 어딘지 확인하는 변수  
  38.     HWND m_hFocus;  
  39.   
  40.     bool m_bInit;  
  41.   
  42.     HWND _GetEditHwnd(void);  
  43.     HWND _GetListHwnd(void);  
  44.     HWND _GetParentHwnd(void);  
  45. };  


EditListCtrl.cpp
  1. // EditListCtrl.cpp : 구현 파일입니다.  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include "TestApp.h"  
  6. #include "EditListCtrl.h"  
  7.   
  8.   
  9. // CEditListCtrl  
  10.   
  11. IMPLEMENT_DYNAMIC(CEditListCtrl, CListCtrl)  
  12.   
  13. CEditListCtrl::CEditListCtrl()  
  14. : m_nItem(0), m_nSubItem(0)  
  15. , m_bInit(false)  
  16. , m_hFocus((HWND)0)  
  17. {  
  18.   
  19. }  
  20.   
  21. CEditListCtrl::~CEditListCtrl()  
  22. {  
  23. }  
  24.   
  25. BEGIN_MESSAGE_MAP(CEditListCtrl, CListCtrl)  
  26.     ON_NOTIFY_REFLECT(NM_CLICK, &CEditListCtrl::OnNMClick)  
  27. END_MESSAGE_MAP()  
  28.   
  29.   
  30.   
  31.   
  32. // CEditListCtrl 메시지 처리기입니다.  
  33.   
  34. BOOL CEditListCtrl::PreTranslateMessage(MSG* pMsg)  
  35. {  
  36.     if(m_hFocus != (HWND)0)  
  37.     {  
  38.         HWND hFocus = ::GetFocus();  
  39.   
  40.         // Edit 가 Focus를 잃으면.  
  41.         if(m_hFocus != hFocus)  
  42.         {  
  43.             OnOK();  
  44.         }  
  45.     }  
  46.       
  47.   
  48.     return CListCtrl::PreTranslateMessage(pMsg);  
  49. }  
  50.   
  51. void CEditListCtrl::Init(void)  
  52. {  
  53.     if(m_bInit) return;  
  54.   
  55.     SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);  
  56.     ::ShowWindow(_GetEditHwnd(), SW_HIDE);  
  57.   
  58.     m_bInit = true;  
  59. }  
  60.   
  61.   
  62. // Return 키가 눌렸을때 호출하기 위한..  
  63. void CEditListCtrl::OnOK(void)  
  64. {  
  65.     CWnd* pWnd = GetFocus();  
  66.   
  67.     if(_GetEditHwnd() == pWnd->GetSafeHwnd() ||  
  68.         _GetEditHwnd() == m_hFocus)  
  69.     {  
  70.         // EditCtrl로 부터 Text를 얻어온다.  
  71.         wchar_t cText[255];  
  72.         ZeroMemory(cText, 255);  
  73.         ::GetWindowText(_GetEditHwnd(), cText, 255);  
  74.         CString cStrText(cText);  
  75.   
  76.         // 얻어온 텍스트를 ListCtrl 에 적용시킨다  
  77.         if(SetText(cStrText, m_nItem, m_nSubItem) == FALSE)  
  78.             return;  
  79.   
  80.         // EditCtrl의 Focus를 죽인다.  
  81.         ::SendMessage(_GetEditHwnd(), WM_KILLFOCUS, 0, 0);  
  82.         ::ShowWindow(_GetEditHwnd(), SW_HIDE);  
  83.         ::SetFocus(pWnd->GetSafeHwnd());  
  84.   
  85.         // ListCtrl의 SelectionMark를 지정한다.  
  86.         SetSelectionMark(m_nItem);  
  87.     }  
  88.   
  89.     m_hFocus = (HWND)0;  
  90. }  
  91.   
  92. BOOL CEditListCtrl::SetText(CString _val, int _row, int _col)  
  93. {  
  94.     return SetItemText(_row, _col, _val);  
  95. }  
  96.   
  97. void CEditListCtrl::OnNMClick(NMHDR *pNMHDR, LRESULT *pResult)  
  98. {  
  99.     Invalidate();  
  100.   
  101.     LPNMITEMACTIVATE pNMITEM = (LPNMITEMACTIVATE) pNMHDR;  
  102.     m_nItem = pNMITEM->iItem;  
  103.     m_nSubItem = pNMITEM->iSubItem;  
  104.   
  105.     // 선택된 지점이 올바르지 않을시...  
  106.     if(m_nSubItem == 0 || m_nSubItem == -1 || m_nItem == -1)  
  107.         return;  
  108.   
  109.     // 선택된 SubItem의 Text를 CEdit 에 넣어주기 위한 변수  
  110.     CString cStrText = GetItemText(m_nItem, m_nSubItem);  
  111.   
  112.     CRect rtSubItem, rtListCtrl, rtDlg;  
  113.     // SubItem 의 Rect를 얻어온다.  
  114.     if(GetSubItemRect(pNMITEM->iItem, pNMITEM->iSubItem, LVIR_BOUNDS, rtSubItem) == FALSE)  
  115.         return;  
  116.   
  117.     // ListControl 의 Window Bounds 를 얻어온다.  
  118.     ::GetWindowRect(_GetListHwnd(), &rtListCtrl);  
  119.   
  120.     // Parent Dialog 의 Windows Bounds 를 얻어온다.  
  121.     ::GetWindowRect(_GetParentHwnd(), &rtDlg);  
  122.   
  123.     // Dialog에 위치한 ListCtrl의 left & top 위치를 구한다.  
  124.     int nThisLeft = rtListCtrl.left - rtDlg.left;  
  125.     int nThisTop = rtListCtrl.top - rtDlg.top;  
  126.   
  127.     // EditCtrl의 위치를 변경해준다.  
  128.     ::SetWindowPos(  
  129.         _GetEditHwnd(),   
  130.         HWND_TOP,  
  131.         nThisLeft + rtSubItem.left + 7,  
  132.         nThisTop + rtSubItem.top + 4,  
  133.         rtSubItem.Width() - 7,  
  134.         rtSubItem.Height() - 4,  
  135.         NULL);  
  136.   
  137.     // EditCtrl Show  
  138.     ::ShowWindow(_GetEditHwnd(), SW_SHOW);  
  139.     ::SetFocus(_GetEditHwnd());  
  140.   
  141.     // SubItem 위치에 외각선을 그려준다.  
  142.     ::Rectangle(::GetDC(_GetListHwnd()), rtSubItem.left, rtSubItem.top - 1, rtSubItem.right, rtSubItem.bottom);  
  143.   
  144.     // EditCtrl 에 선택한 List의 Text 를 넣어준다.  
  145.     ::SetWindowText(_GetEditHwnd(), cStrText);  
  146.   
  147.     // 삽입한 Text의 마지막에 포커스를 맞춰주고 Mark한다.  
  148.     int nSel = cStrText.GetLength();  
  149.     ::SendMessage(_GetEditHwnd(), EM_SETSEL, LOWORD(nSel), HIWORD(nSel));  
  150.   
  151.     // Focus를 잃었을 경우 ListCtrl에 적용시키기 위해 저장  
  152.     m_hFocus = _GetEditHwnd();  
  153.     Invalidate();  
  154.   
  155.     *pResult = 0;  
  156. }  
  157.   
  158.   
  159. HWND CEditListCtrl::_GetEditHwnd(void)  
  160. {  
  161.     return ::GetDlgItem(GetParent()->GetSafeHwnd(), IDC_EDIT_CAM_ATTR);  
  162. }  
  163.   
  164. HWND CEditListCtrl::_GetListHwnd(void)  
  165. {  
  166.     return GetSafeHwnd();  
  167. }  
  168.   
  169. HWND CEditListCtrl::_GetParentHwnd(void)  
  170. {  
  171.     return GetParent()->GetSafeHwnd();  
  172. }  



이 컨트롤을 가지는 Dialog Class의 PreTranslateMessage 부분 혹은 OnOK 부분 (Return키 작동시를 위한)에
현재 포커스가 ListCtrl 혹은 EditCtrl 일 경우 CEditListCtrl 의 OnOK 함수를 호출해야 합니다.

  1. CTestDlg::OnOK()  
  2. {  
  3.     int id = GetFocus()->GetDlgCtrlID();  
  4.     if(id == IDC_LIST_EDITLISTCTRL || id == IDC_EDIT_EDITLISTCTRL)  
  5.         m_editListCtrl.OnOK()  
  6. }  


이정도가 되겠습니다.


반응형
반응형

이미 CMFCShellListCtrl 내부에서 더블클릭 이벤트를 처리하고 있기 때문입니다.

 

CMFCShellListCtrl 에서 상속받은 MyShellListCtrl 클래스 하나 만드시고 

 

NM_DBLCLK Notify Reflect 이벤트 핸들러 등록하신 다음 거기서 처리해보세요.

 

ON_NOTIFY_REFLECT(NM_DBLCLK, &MyShellListCtrl::OnNMDblclk)

 

MyShellListCtrl::OnNMDblclk 안에서는 다음 코드를 한번 불러줘야 원래 동작이 실행됩니다.

 

CMFCShellListCtrl::OnDblClk(pNMHDR, pResult);






http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=50&MAEULNo=20&no=511226&ref=511179


1. MFC를 사용하시려면

  다이얼로그 편집창에서 Ctrl+W 눌러서 클래스위저드를 실행하구요

  Member varible 탭에서 IDC_LIST_USER 을 더블클릭하여

  멤버이름을 m_ListUser로 카테고리를 Control로 지정하고 확인버튼을 눌러 닫습니다.

 

  ----소스코드 수정----

//    HWND hWnd1 =  ::GetDlgItem (m_hWnd,IDC_LIST_USER);

    LPNMITEMACTIVATE temp = (LPNMITEMACTIVATE) pNMHDR;

    RECT rect;

    //get the row number

    int nItem = temp->iItem;

    //get the column number

    

    int nSubItem = temp->iSubItem;

    if(nSubItem == 0 || nSubItem == -1 || nItem == -1)

        return ;

    //Retrieve the text of the selected subItem from the list

    CString str = m_ListUser->GetItemText(nItem ,nSubItem);

 

2. API로 코딩하시려면

    GetItemText 대신에 ListView_GetItemText 함수를 사용하셔야 합니다.

 

   ---- 소스코드 수정----

  HWND hWnd1 =  ::GetDlgItem (m_hWnd,IDC_LIST_USER);

    LPNMITEMACTIVATE temp = (LPNMITEMACTIVATE) pNMHDR;

    RECT rect;

    //get the row number

    int nItem = temp->iItem;

    //get the column number

    

    int nSubItem = temp->iSubItem;

    if(nSubItem == 0 || nSubItem == -1 || nItem == -1)

        return ;

    //Retrieve the text of the selected subItem from the list

    CString str;

    ListView_GetItemText(hWnd1,nItem ,nSubItem, str, 255);

 

둘 중에서 편한방법으로 코딩하세요...^^



반응형
반응형

http://sehwa4444.egloos.com/2728018



다이얼로그에는 OnIdle은 없지만 내부적으로 UI업데이트를 위한 WM_KICKIDLE 메시지를 가지고 있다
WM_KICKIDLE 메세지는 UNDOCUMENT 메세지로 idle상태일때 보내지는 메세지며 위자드에서 지원하지 않기 때문에 직접 넣어줘야한다.

 
// -- DMyDlg.h --

#include <afxpriv.h>


CMyDialog : public CDialog
{
...
protected:
   ... // other message handler
   afx_msg LRESULT OnKickIdle(WPARAM wParam, LPARAM lParam);
   ...
};

 


// -- DMyDlg.cpp --

BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
    //{{AFX_MSG_MAP(CMyDialog)
    ON_MESSAGE(WM_KICKIDLE, OnKickIdle)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

LRESULT CMyDialog::OnKickIdle(WPARAM wParam, LPARAM lParam)
{
   UpdateDialogControls(this, FALSE);


   return (LRESULT)1;
}

반응형
반응형

http://icary.tistory.com/5



프로그램에서 편하게 Log를 남길 수 있는 class 입니다.

Singleton을 사용해서 multi thread 환경에서도 하나의 객체만 생성되며,

Log파일의 이름은 발생 날짜 정보와 실행파일의 이름을 조합하여 사용합니다.

Log는 argument list를 사용해서 printf에서 사용하는 다음의 format을 사용할 수 있습니다.
- %d : int
- %c : char
- %s : char *
더 많은 변수를 사용하려면 LogManager.cpp에서 다음 부분을 수정해서 사용하면 됩니다.
    va_start( argptr, mesg );
    for(int i = 0; i < format.GetLength(); i++){
        if(format.GetAt(i) != '%'){

   buffer2 = " ";
   buffer2.SetAt(0, format.GetAt(i));
            buffer += buffer2;
        } else {
            i++;
            switch(format.GetAt(i)){
                case 'd':
                    buffer2.Format("%d", va_arg(argptr,int) );
                    buffer += buffer2;
                    break;
                case 's':
                    buffer2.Format("%s", va_arg(argptr, char*) );
                    buffer += buffer2;
                    break;
                case 'c':
                    buffer2.Format("%c", va_arg(argptr, char) );
                    buffer += buffer2;
                    break;
            }
        }
    }
    va_end( argptr );

사용방법은 
1. LogManager.zip 을 다운 받는다.
2. LogManager.zip 의 압축을 푼다.
3. LogManager.h와 LogManager.cpp을 프로젝트에 추가한다.
4. 로그를 남기고 싶은 곳에서 
LogManager::GetInstance()->Log("Put your Log Here!!"); 
과 같은 방식으로 사용한다.

첨부파일

반응형
반응형


http://warmz.tistory.com/872


CView 클래스에서 WM_SETCURSOR 메시지를 추가하고 아래와 같이 사용한다.

선언

1
2
3
4
5
6
7
8
9
10
11
12
13
// Cursor Style for Shape control
enum CursorStyle {
    CursorArrow = 0,
    CursorCross,
    CursorInputText,
    CursorSizeAll,
    CursorSizeNESW,     // 우측상단에서 좌측하단
    CursorSizeNS,       // 수직
    CursorSizeNWSE,     // 좌측상단에서 우측하단
    CursorSizeWE        // 수평
};
 
HCURSOR m_CursorStyleList[8];

아이콘 할당

1
2
3
4
5
6
7
8
9
10
11
CTestView::CTestView()
{
    m_CursorStyleList[CursorArrow] = LoadCursor(NULL, IDC_ARROW);
    m_CursorStyleList[CursorCross] = LoadCursor(NULL, IDC_CROSS);
    m_CursorStyleList[CursorInputText] = LoadCursor(NULL, IDC_IBEAM);
    m_CursorStyleList[CursorSizeAll] = LoadCursor(NULL, IDC_SIZEALL);
    m_CursorStyleList[CursorSizeNESW] = LoadCursor(NULL, IDC_SIZENESW);
    m_CursorStyleList[CursorSizeNS] = LoadCursor(NULL, IDC_SIZENS);
    m_CursorStyleList[CursorSizeNWSE] = LoadCursor(NULL, IDC_SIZENWSE);
    m_CursorStyleList[CursorSizeWE] = LoadCursor(NULL, IDC_SIZEWE);
}

아이콘 지정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
BOOL CDrawingToolView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
    CPoint pt;
     
    GetCursorPos(&pt);
    ScreenToClient(&pt);
 
    if(m_rect.PtInRect(pt))
    {
        SetCursor(m_CursorStyleList[CursorSizeAll]);
    }
    else
    {
        return __super::OnSetCursor(pWnd, nHitTest, message);
    }
 
    return TRUE;
}
 


아이콘의 종류




*손모양은 IDC_HAND

반응형
반응형


// 선언
afx_msg LRESULT OnPropertyChanged(WPARAM wparam, LPARAM lparam);

// 메시지 맵
BEGIN_MESSAGE_MAP(Page1, PropertyPageBase)
ON_REGISTERED_MESSAGE(AFX_WM_PROPERTY_CHANGED, OnPropertyChanged)
END_MESSAGE_MAP()

// Property 추가
CMFCPropertyGridProperty* pProp1 = new CMFCPropertyGridProperty(strTitle, (_variant_t)bVal, strDesc);
assert(pProp1 );
if(pProp1  == 0) return false;
pProp1->SetData(100); // 메시지 구분을 위해 사용한다.
m_wndPropList.AddProperty(pProp1);

// 메시지 처리
LRESULT Page1::OnPropertyChanged(WPARAM wparam, LPARAM lparam)
{
CMFCPropertyGridProperty *pProp = (CMFCPropertyGridProperty*)lparam;
if (!pProp) return 0;

switch(pProp->GetData())
{
case 100:
// 메시지 처리
break;
}
}

////////////////////////////////////////////////////////////////////////////////////////////
// Boolean 속성
////////////////////////////////////////////////////////////////////////////////////////////
// 생성
CMFCPropertyGridProperty* pBooleanProp1 = new CMFCPropertyGridProperty(strTitle, (_variant_t)true or false, strDesc);
pBooleanProp1->SetData(dwID);
m_wndPropList.AddProperty(pBooleanProp1);
// 속성 지정
CMFCPropertyGridProperty* pBooleanProp1 = m_wndPropList.FindItemByData(dwID);
pBooleanProp1->SetValue((_variant_t)true or false);
// 속성 가져오기
COleVariant rVariant = pProperty->GetValue();
VARIANT v = rVariant.Detach();
VARTYPE vt = v.vt;
assert(vt == VT_BOOL);
#pragma warning(disable:4800)
bool bVal = (bool)v.boolVal;
#pragma warning(default:4800)

////////////////////////////////////////////////////////////////////////////////////////////
// Dropdown Combobox
////////////////////////////////////////////////////////////////////////////////////////////
// 생성 : 초기값이 옵션에 없어도 초기값으로 출력됨, 이 경우 초기값은 추가되지 않고 콤보박스에서 선택안됨
CMFCPropertyGridProperty* pDropdownCombo1
= new CMFCPropertyGridProperty(_T("타이틀"), _T("초기값"), _T("설명"));
pDropdownCombo1->AddOption(_T("옵션1"));
pDropdownCombo1->AddOption(_T("옵션2"));
pDropdownCombo1->AllowEdit(FALSE);
pDropdownCombo1->SetData(dwID);
m_wndPropList.AddProperty(pDropdownCombo1);
// 속성 지정
CMFCPropertyGridProperty* pDropdownCombo1 = m_wndPropList.FindItemByData(dwID);
pDropdownCombo1->SetValue(_T("옵션2"));
// 속성 가져오기
USES_CONVERSION;
COleVariant rVariant = pProperty->GetValue();
VARIANT v = rVariant.Detach();
VARTYPE vt = v.vt;
assert(vt == VT_BSTR);
CString strW = OLE2W(v.bstrVal);

////////////////////////////////////////////////////////////////////////////////////////////
// Integer
////////////////////////////////////////////////////////////////////////////////////////////
// 생성 : 초기값 또는 속성 지정시에 int 또는 long int형을 혼용하지 말고 한 가지로 통일해서 써야 한다.
// 다른 데이터형에 대한 경우에도 물론 혼용은 하지 않는다.
// 기본값이 아닌 값에 대해 bold로 표시하고 싶은 경우는 long int형을 써야 한다.
// 다만 Spinner 속성을 지정하는 경우에는 아무때나 int를 써도 무방하다.
CMFCPropertyGridProperty* pIntegerProp1 = new CMFCPropertyGridProperty(strTitle, (_variant_t)(long int)nVal, strDesc);
pIntegerProp1->AllowEdit(TRUE); // or FALSE
pIntegerProp1->SetData(dwID);
pIntegerProp1->EnableSpinControl(TRUE, -10, 10);
m_wndPropList.AddProperty(pIntegerProp1);
// 속성 지정
CMFCPropertyGridProperty* pIntegerProp1 = m_wndPropList.FindItemByData(dwID);
pIntegerProp1->SetValue((_variant_t)(long int)nVal);
// 속성 가져오기
COleVariant rVariant = pProperty->GetValue();
VARIANT v = rVariant.Detach();
VARTYPE vt = v.vt;
assert(vt == VT_I4);
#pragma warning(disable:4800)
int nVal = (int)v.intVal;
#pragma warning(default:4800)

////////////////////////////////////////////////////////////////////////////////////////////
// Float
////////////////////////////////////////////////////////////////////////////////////////////
// 생성
CMFCPropertyGridProperty* pFloatProp1 = new CMFCPropertyGridProperty(strTitle, (_variant_t)fVal, strDesc);
pFloatProp1->AllowEdit(TRUE); // or FALSE
pFloatProp1->SetData(dwID);
m_wndPropList.AddProperty(pFloatProp1);
// 속성 지정
CMFCPropertyGridProperty* pFloatProp1 = m_wndPropList.FindItemByData(dwID);
pFloatProp1->SetValue((_variant_t)fVal);
// 속성 가져오기
COleVariant rVariant = pProperty->GetValue();
VARIANT v = rVariant.Detach();
VARTYPE vt = v.vt;
assert(vt == VT_R4);
#pragma warning(disable:4800)
float fVal = (float)v.fltVal;
#pragma warning(default:4800)

///////////////////////////////////////////////////////////////////////////////////////////////////
// Text Edit
///////////////////////////////////////////////////////////////////////////////////////////////////
// 생성
CMFCPropertyGridProperty* pStringProp1 = new CMFCPropertyGridProperty(strTitle, (_variant_t)strVal, strDesc);
pStringProp1->AllowEdit(TRUE); // or FALSE
pStringProp1->SetData(dwID);
m_wndPropList.AddProperty(pStringProp1);
// 속성 지정
CMFCPropertyGridProperty* pStringProp1 = m_wndPropList.FindItemByData(dwID);
pStringProp1->SetValue((_variant_t) strVal);
// 속성 가져오기
USES_CONVERSION;
COleVariant rVariant = pProperty->GetValue();
VARIANT v = rVariant.Detach();
VARTYPE vt = v.vt;
assert(vt == VT_BSTR);
CString strW = OLE2W(v.bstrVal);

////////////////////////////////////////////////////////////////////////////////////////////
// Font : 속성 변경시 메시지가 여러 번 온다.
////////////////////////////////////////////////////////////////////////////////////////////
// 생성
CMFCPropertyGridFontProperty* pFont1
= new CMFCPropertyGridFontProperty(strTitle, LOGFONT, CF_EFFECTS | CF_SCREENFONTS, strDesc);
pFont1->SetData(dwID);
LOGFONT* pFont = pFont1->GetLogFont();
COLORREF hColor = pFont1->GetColor();
memcpy(pFontOut, pFont, sizeof(LOGFONT)); // 폰트속성 저장(땜빵해주기)
*pColorOut = hColor; // 폰트색상 저장(땜빵해주기)
m_wndPropList.AddProperty(pFont1);
// 속성 지정
CMFCPropertyGridFontProperty* pFontProp1 = (CMFCPropertyGridFontProperty*)m_wndPropList.FindItemByData(dwID);
LOGFONT* pFont = pFontProp1->GetLogFont();
lstrcpy(pFont->lfFaceName, strW); // 속성 지정(API를 통하지 않고 직접 수정한다)
m_wndPropList.UpdateData(FALSE); // 속성 업데이트
*pColorOut = pFontProp1->GetColor(); // 폰트속성 저장(땜빵해주기)
memcpy(pFontOut, pFont, sizeof(LOGFONT)); // 폰트색상 저장(땜빵해주기)
// 속성 가져오기
USES_CONVERSION;
LPLOGFONT pFont1;
COLORREF FontColor1;
COleVariant rVariant = pFontProperty->GetValue();
VARIANT v = rVariant.Detach();
VARTYPE vt = v.vt;
assert(vt == VT_BSTR);
LOGFONT* pFont1 = pFontProperty->GetLogFont();
COLORREF hColor = pFontProperty->GetColor();
if(hColor != *pColorOut || (memcmp(pFontOut, pFont1, sizeof(LOGFONT)) != 0))
{
// 이벤트 처리(이전에 저장해둔 값들과 틀린 경우)
}

////////////////////////////////////////////////////////////////////////////////////////////
// Color
////////////////////////////////////////////////////////////////////////////////////////////
// 생성
CMFCPropertyGridColorProperty* pColor1 = new CMFCPropertyGridColorProperty(strTitle, hColor, NULL, strDesc);
pColor1->SetData(dwID);
pColor1->EnableOtherButton(_T("기타...")); // "기타..." 버튼을 활성화 시킴
pColor1->AllowEdit(TRUE);
m_wndPropList.AddProperty(pColor1);
// 속성 지정
CMFCPropertyGridColorProperty* pColorProp1 = (CMFCPropertyGridColorProperty*)m_wndPropList.FindItemByData(dwID);
pColorProp1->SetColor(hColor);
// 속성 가져오기
COleVariant rVariant = pProperty->GetValue();
VARIANT v = rVariant.Detach();
VARTYPE vt = v.vt;
assert(vt == VT_I4);
COLORREF hColor = pProperty->GetColor();

////////////////////////////////////////////////////////////////////////////////////////////
// File Select, Folder Select : 처음 메시지가 두 번 오는 경우가 허다하다. 두 번째 부터는 제대로 오는 듯 하다.
////////////////////////////////////////////////////////////////////////////////////////////
// 생성
// File select
CMFCPropertyGridFileProperty* pFileSelect1
= new CMFCPropertyGridFileProperty(strTitle, TRUE, _T(""), 0, 0, strFilter, strDesc);
// Folder select
//CMFCPropertyGridFileProperty* pFileSelect1 = new CMFCPropertyGridFileProperty(strTitle, strInitialFolder);
pFileSelect1->SetData(dwID);
USES_CONVERSION;
wchar_t wbuf[1024];
COleVariant rVariant = pFileSelect1->GetValue();
VARIANT v = rVariant.Detach();
VARTYPE vt = v.vt;
assert(vt == VT_BSTR);
swprintf_s(wbuf, L"%s", OLE2W(v.bstrVal));
CStringW strW = wbuf; // Font 의 경우처럼 미리 저장해 놓는다.
m_wndPropList.AddProperty(pFileSelect1);
// 속성 지정
CMFCPropertyGridFileProperty* pFile1 = (CMFCPropertyGridFileProperty*)m_wndPropList.FindItemByData(dwID);
pFile1->SetValue((_variant_t)strFile);
CStringW strW = strFile; // 속성을 따로 저장
// 속성 가져오기
USES_CONVERSION;
wchar_t wbuf[1024];
COleVariant rVariant = pProperty->GetValue();
VARIANT v = rVariant.Detach();
VARTYPE vt = v.vt;
assert(vt == VT_BSTR);
swprintf_s(wbuf, L"%s", OLE2W(v.bstrVal));
CString strFile1 = wbuf;
if(strW != strFile1)
{
// 이벤트 처리(이전에 저장해둔 값들과 틀린 경우)
strW = wbuf;
}


폰트, 파일 관련 프로퍼티 변경시, 한 번의 이벤트에 메시지가 여러 번 온다.
패치 안되나...

반응형
반응형

http://npteam.net/43


void Cpaint1View::OnContextMenu(CWnd* pWnd, CPoint point)
{
  CMenu entire;
  CMenu *popup;
  
  entire.LoadMenu( IDR_MAINFRAME );
  popup = entire.GetSubMenu( 3 );
  popup->TrackPopupMenu( TPM_LEFTALIGN TPM_RIGHTBUTTON, point.x, point.ythis );
}

반응형
반응형

http://cafe.naver.com/visualtools/18


--------------------------------------------------------------------------

MainFrame.h

--------------------------------------------------------------------------

:

// Generated message map functions
protected:
 //{{AFX_MSG(CMainFrame)
 afx_msg void OnSysCommand(UINT nID, LPARAM lParam); // 추가할 부분
 afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
 //}}AFX_MSG
 DECLARE_MESSAGE_MAP()

--------------------------------------------------------------------------

MainFrame.cpp

--------------------------------------------------------------------------

 

BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
 //{{AFX_MSG_MAP(CMainFrame)
 ON_WM_SYSCOMMAND()    // 추가할 부분
 ON_WM_CREATE()
 //}}AFX_MSG_MAP
END_MESSAGE_MAP()

:

:

 

// 아래의 함수를 추가한다.

//*****************************************************************************/
//* 1. Operation        : OnSysCommand
//* 2. Description      : 시스템버튼을 선택했을 때의 처리
//* 3. Parameter        : 
//* 4. Return Value     : 
//* 5. Remark           : 
//*****************************************************************************/
void CMainFrame::OnSysCommand(UINT nID, LPARAM lParam)
{

 int nMsgId = 0;
 
 switch(nID)
 {
 case SC_CLOSE :
  nMsgId = MessageBox("프로그램을 종료하시겠습니까?", "SendMsg", MB_YESNO | MB_ICONQUESTION);
  if(nMsgId == IDNO)
  {
   return;
  }
 case SC_MINIMIZE :           //최소화
 case SC_MAXIMIZE :          //최대화
 case SC_RESTORE :           //최소화에서 최대화로 복구시

  ;
 }
 CFrameWnd::OnSysCommand(nID, lParam);
}


반응형
반응형

http://www.cyworld.com/soinho/2338636



WM_ENTERSIZEMOVE , WM_EXITSIZEMOVE - 마우스의 드래그의 시작 과 종료.





WM_ENTERSIZEMOVE : 마우스의 드래그가 시작되는 시점에서 호출된다.

WM_EXITSIZEMOVE    : 마우스의 드래그가 끝나는 시점..클릭이 업이되는 시점에서 호출된다.

 

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

// 메세지 사용하기.

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

Add:

  • ON_MESSAGE( WM_ENTERSIZEMOVE, OnEnterSizeMove)
  • ON_MESSAGE( WM_EXITSIZEMOVE, OnExitSizeMove)

to your message map.

Put:

  • afx_msg LRESULT OnEnterSizeMove (WPARAM, LPARAM);
  • afx_msg LRESULT OnExitSizeMove (WPARAM, LPARAM);

in your header file.

Handle the messages returning zero as follows:

LRESULT OnEnterSizeMove( WPARAM wparam, LPARAM lparam) 
{     
// do stuff      
	return (LRESULT)0; 
} 
 LRESULT OnExitSizeMove( WPARAM wparam, LPARAM lparam) 
{     
// do stuff      
	return (LRESULT)0; 
}

반응형
반응형

http://blog.naver.com/san008/40013444714


키보드 이벤트 처리하기

본장은 키보드로부터 문자키와 펑션키등이 입력되었을 때 이벤트를 처리하는 방법에 대해서 설명합니다. 키보드에 있는 모든키와 시스템에서 사용하는 키들을 직접 제어 할수 있는 방법을 배우시게 될것입니다. 한들윈도우에서는 한영전환이 자동으로 내장되어 있습니다. 키보드 입력키로 현재 한글상태인지 영문상태인지 확인할 수가 없습니다. 이것을 확인하는 방법은 IME를 이용하는 것인데 본장에서는 키보브에서 한영 전환이 되는 것을 알아보는 방법까시 설명하게습니다.

본장을 통해서 키보드에서 발생하는 모든 이벤트를 만드는 방법을 알수 있을것입니다.


키보드 입력 메시지

키보드로부터 키가 입력될때에는 표1과 같은 내용의 메시지들이 전달됩니다.

(표1) 키눌림 메시지


  메시지

 내용

 WM_CHAR

 문자키가 눌려졌  을경우

 WM_KEYDOWN

 키보드에서 키가 눌려짐

 WM_KEYUP

 키보드에서 키가 눌려졌다 띄어짐

 WM_SYSKEYDOWN

 Alt키와 조합된 시스템 키가 눌려짐

 WM_SYSKEYUP

 Alt키와 조합된 시스템 키가 눌려졌다 띄어짐

WM_CHAR은 문자키가 눌려졌을 때 발생하는 메시지입니다. 보통 알파벳과 한글 그리고 숫자키와 등 ANSI 코드에 포함된 키들의 눌려졌을 때 발생하는 메시지입니다. 쉽게 말해서 F1-F12키나 또는 Insert,Home,화살표키등을 제외한 일반적으로 화면에 나타나는 키들을 생각하시면 됩니다.

WM_KEYDOWN은 키보드에서 키가 눌려 졌을 경우 발생됩니다. 키가 눌려진 상태에서는 WM_KEYDOWN이 키를 띄었을 경우 WM_KEYUP가 발생됩니다.

만일 'a'라는 키가 눌렀다 띄었다고 가정을 한다면 제일 처음으로 발생되는 메시지가 WM_KEYDOWN입니다. 이후에 WM_CHAR가 발생되고 그다음 WM_KEYUP가 발생됩니다. 만일 Shift키를 누르면서 'A'키를 키보드에서 설정하였다면 어떻게 메시지가 발생될까요? 먼저 Shift키가 눌려졌다는 WM_KEYDOWN 이 발생되며 다음으로 'A'에 해당하는 WM_KEYDOWN 그다음으로 ‘A'에 해당하는 WM_CHAR 그다음으로 'A' 에 해당하는 WM_KEYUP 그다음으로 Shift 에 해당하는 WM_KEYUP 가 발생됩니다. 이런식으로 WM_CHAR와 WM_KEYDOWN과 WM_KDYUP는 상호 연관되어 발생됩니다. 그러나 F1키 같은 펑션키를 눌렀을경우에는 WM_CHAR은 발생되지 않습니다.

WM_SYSKEYDOWN은 시스템 키를 눌렀을 때 발생됩니다. 이키란 Alt와 함께 조합된 키늘 말합니다. Alt+F4는 종료키며 Alt+Tab는 어플리케이션 프로그램의 상호 전환인 것 같은 Alt와 조합된 키를 시스템 키라고 하며 이것은 특별하게 WM_SYSKEYDOWN과 WM_SYSKEYUP로 분리하여 메시지를 설정하였습니다.

WinMain에서  다음과 같은 문장을 보신적이 있을것입니다.

while(GetMessage(&msg,NULL,0,0))

{

       TranslateMessage(&msg);

       DispatchMessage(&msg);

}

여기에서 TranslateMessage는 WM_KEYDOWN을 받아서 현재 눌려지 키의 값을 가지고 문자를 만들어 내는 기능을합니다. 예를 들어 Caps Lock가 켜지지 않은 상태에서  Shift 키를 누른상태에서 A자를 눌렀다면 실제 코드는 “A"입니다. 그러나 Shift키를 누르지 않은 상태에서 A자를 눌렀다면 이코드는 ”a"인것입니다. 이렇게 TranslateMessage는 WM_KEYDOWN에의해서 WM_CHAR를 만들어 내는 작업을 합니다. WM_CHAR이나 WM_KEYDOWN 메시지와 함께 넘겨주는 wParam에는 key값이 설정되어 있습니다. lParam에는 이키들의 정보가 들어 있는데 키의 정보는 표2와 같습니다.


    바이트 번호

 (상위첫바이트가 0)

 키 값

 내용

  0

 Transition Statate

 키가 눌려지면 1 띄어지면 0

  1

 Previous Key State

 이전에 어떤 키가 눌려졌는가에 대한 값

 2

 Context Code

 Alt키가 눌려지면 1 그렇지 않으면 0

 3-6

 사용하지 않음

 

  7

 Extended Key Flag

 확장 키보드에서 키가 눌려지면

  1 그렇지 않으면 0

  8-15

 OEM Scan Code

 키보드 스캔코드

  16-31

 Repeat Count

 킷값의 반복횟수

       (표2) lParam 에 설정되어 있는 값


lParam의 상위 0비트가 1로 설정되어 있으면 키가 눌려진것이고 0이면 키가 띄어진 형태입니다. 두 번째 비트는 Previous Key State로 이전에 어떤 키가 눌려졌는가에 대한 값입니다. 이런 의미라고 생각한다면 어플리케이션이 처음 시작되고 키를 누른다음부터는 Previous Key State는 어떤 키가 눌려진상태는 0이고 띈상태에서 1로 설정됩니다. Contex Code란 Alt키가 눌려졌을 경우 1로 그렇지 않을 경우 0이됩니다. Extended Key Flag는 확장키보드 즉 숫자판이 있는 키보드에서 NumLock나 / 또는 Enter키가 눌려졌을 경우 1로 그렇지 않을 경우 0으로 설정됩니다. OEM Scan Code는 현재 눌려진 키보드 스캔코드 값입니다. 일반적으로 문자 값으로 보아도 되지만 윈도우에서느 가상키를 사용하고 이가상키는 wParam으로부터 얻을수 있고 또한 문자값은 WM_CHAR와 함께 발생된 wParam으로부터 얻을수 있기 때문에 사용하지 않습니다. 16-31은 킷값의 반복횟수가 저장됩니다. 이것은 키를 누른 메시지가 처리되기전에 킷값이 또 입력되었을 때 연속적으로 메시지 큐에 저장하는 것이 아니라 WM_KEYDOWN 메시지를 하나로 묶고 반복 계수를 증가시킴으로써 현재 키가 몇번의 반복으로 눌려졌다는 것을 알려줍니다.

이반복 계수를 무시하게 되면 키를 연속적으로 몇번을 눌러도 그것이 수행되지 않았을 경우 잃어 버리게 됩니다. 결국 WM_MOUSEMOVE처럼 현재성을 중시하여 이전에 들어왔던 메시지가 무시되는 것입니다. 그러나 키보드 입력에서는 킷값을 놓치지 말아야 할경우가 있습니다. 이럴경우에는 반복계수를 확인하여 그만큼 연속적으로 키를 설정해 주어야 합니다.


가상키 코드

가상 키코드란 windows.h에 정의한 키코드 값들입니다. 표3은 가상키의 일부분입니다.

  키 코드

  내용

 코드값

 VK_CANCEL

 Ctrl+Break를 눌렀을때

 03

 VK_BACK

 Backspace 키

 08

 VK_TAB

 Tab키

 09

 VK_RETURN

 Enter 키

 13

 VK_SHIFT

 Shift키

 16

 VK_CONTROL

 Ctrl 키

 17

 VK_MENU

 Alt 키

 18

 VK_CAPITAL

 Caps Lock 키

 20

 VK_ESCAPE

 Esc 키

 27

 VK_SPACE

 Space 바

 32

 VK_PRIOR

 Page Up 키

 33

 VK_NEXT

 Page Down 키

 34

 VK_END

 End 키

 35

 VK_HOME

 Home 키

 36

 VK_LEFT

 좌측 화살표

 37

 VK_UP

 위쪽 화살표

 38

 VK_RIGHT

 우측 화살표

 39

 VK_DOWN

 아랫쪽 화살표

 40

 VK_INSERT

 Insert키

 45

 VK_DELETE

 Delete키

 46

 VK_F1 ~ VKF10

 F1-F10

 112-121

 VK_NUMLOCK

 Num Lock

 144

 VK_SCROLL

 Scroll Lock

 145

        (표3) 가상키 코드


WM_KEYDOWN 키가 눌려졌을 경우 해당되는 키가 눌려진 것에 대한 프로그램을 제작한다면 위의 정의 코드를 이용하여 다음과 같이 할수 있습니다.


case WM_KEYDOWN:

       switch(wParam)

       {

               case VK_F1:

                               //F1 키가 눌려졌을 때 내용

                               break;

               case VK_F2:

                               //F2 키가 눌려졌을 때 내용

                               break;

               case VK_RETURN:

                               //Enter 키가 눌려졌을 때 내용

                               break;

               case VK_SPACE:

                               //Space 바가 눌려졌을 때 내용

                               break;

               :

       }

       break;


이외에서 마우스 버튼이 눌려지게 되면 VK_LBUTTON,VK_RBUTTON,VK_MBUTTON의 값이 들어오게 됩니다. 여기에서 VK_MBUTTON은 마우스 중간 버튼을 의미하는데 이값이 들어오기 위해서는 특정 마우스 드라이버가 설정되어 있거나 또는 다른 방법에 의해서 중앙버튼이 활성화 된상태에서만 가능합니다. 실제로 윈도우에서는 마우스 중앙버튼을 거히 사용하지 않기 때문입니다.


키상태 얻기

WM_KEYDOWN 이나 WM_SYSKEYDOWN은 Shift키나 Ctrl키 Alt키 Caps Lock키 Scroll Lock키등의 눌려진 상태를 wParam이나 또는 lParam을 통해서 알려주지 않습니다. 이런 키들이 눌려진 상태를 알고자 할 경우 GetKeyState 함수를 사용합니다.


SHORT GetKeyState(

int nVirtKey //가상키값

); 


예를 들어서 Shift키를 가 눌려졌는가를 알기 위해서는 다음과 같이 합니다.


short code;

code=GetKeyState(VK_SHIFT);

만일 code가 음수일경우에는 Shift키가 눌려진것이고 음수가 아닐경우에는 눌려지지 않은것입니다. GetKeyState함수는 표3에 정의되어 있지않는 특수한 경우의 키상태로 알수 있습니다. 즉 좌측 Alt키 우측 Alt 좌측 Shift 우측 Shift 좌측 Ctrl키 우측 Ctrl 키등을 얻을수 잇습니다. 이 키에 대한 정의 값은 다음과 같습니다.


VK_LSHIFT VK_RSHIFT

VK_LCONTROL VK_RCONTROL

VK_LMENU VK_RMENU


만일 좌측 Alt키가 눌려진 것을 알고자 한다면 다음과 같이 할수 있을것입니다.


short code;

code=GetKeyState(VK_LMENU);



키정보 얻기 예제 KeyEx

KeyEx는 키가 눌려 졌을 때 lParam에 있는 정보와 wParam에 있는 정보를 화면에 출력하는 예제입니다. lParam은 비트연산을 이용하여 각 비튼의 값의 정보를 얻어서 화면에 출력을 합니다.

(프로그램 소스)

//시스템 키입력 예제

//KeyEx.c

#include <windows.h>


void main();


LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;


int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

                    PSTR szCmdLine, int iCmdShow)

     {

     static char szAppName[] = "KeyEx" ;

     HWND        hwnd ;

     MSG         msg ;

     WNDCLASSEX  wndclass ;


     wndclass.cbSize        = sizeof (wndclass) ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;

     wndclass.lpfnWndProc   = WndProc ;

     wndclass.cbClsExtra    = 0 ;

     wndclass.cbWndExtra    = 0 ;

     wndclass.hInstance     = hInstance ;

     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;

     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;

     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;

     wndclass.lpszMenuName  = NULL ;

     wndclass.lpszClassName = szAppName ;

     wndclass.hIconSm       = LoadIcon (NULL, IDI_APPLICATION) ;


     RegisterClassEx (&wndclass) ;


     hwnd = CreateWindow (szAppName,    

                           "키입력예제:KeyEx",  

                    WS_OVERLAPPEDWINDOW, 

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    NULL,                

                    NULL,                

                    hInstance,           

                           NULL) ;                    


     ShowWindow (hwnd, iCmdShow) ;

     UpdateWindow (hwnd) ;


     while (GetMessage (&msg, NULL, 0, 0))

          {

          TranslateMessage (&msg) ;

          DispatchMessage (&msg) ;

          }

     return msg.wParam ;

     }



LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

     {

     HDC         hdc ;

     PAINTSTRUCT ps ;

        int scancode;

        char temp[80];

 switch (iMsg)

      {

       case WM_CREATE :

            return 0 ;

       case WM_PAINT :

          hdc = BeginPaint (hwnd, &ps) ;

          EndPaint (hwnd, &ps) ;

           return 0 ;

     case WM_KEYDOWN:

     case WM_SYSKEYDOWN:

     case WM_KEYUP:

     case WM_SYSKEYUP:

     case WM_CHAR:

       hdc=GetDC(hwnd);

       //문자 코드 얻기

       wsprintf(temp,"문자코드=%c",wParam);

       TextOut(hdc,0,20,temp,strlen(temp));

       //Transition 값 얻기

       ((lParam&0x80000000)==0x80000000) ? wsprintf(temp,"Transition=1") :wsprintf(temp,"Transition=0");

       TextOut(hdc,0,40,temp,strlen(temp));

       //Previous Key 값 얻기

       ((lParam&0x40000000)==0x40000000) ? wsprintf(temp,"PreviousKey=1") :wsprintf(temp,"PreviousKey=0")  ;

       TextOut(hdc,0,60,temp,strlen(temp));

       //ContenxCode 값 얻기

       ((lParam&0x20000000)==0x20000000) ? wsprintf(temp,"ContextCode=1") :wsprintf(temp,"ContextCode=0")  ;

       TextOut(hdc,0,80,temp,strlen(temp));

       //ExtendKey값 얻기

       ((lParam&0x01000000)) ? wsprintf(temp,"ExtendKey=1") :wsprintf(temp,"ExtendKey=0")  ;

       TextOut(hdc,0,100,temp,strlen(temp));

       //Scan code 값 얻기

       scancode=HIWORD(lParam)&0xff;

       wsprintf(temp,"Scancode=%04d",scancode);

       TextOut(hdc,0,120,temp,strlen(temp));

       wsprintf(temp,"RepeatCount=%d",LOWORD(lParam));

       TextOut(hdc,0,140,temp,strlen(temp));

       ReleaseDC(hwnd,hdc);

       return 0;

 case WM_DESTROY :

            PostQuitMessage (0) ;

             return 0 ;

       }


     return DefWindowProc (hwnd, iMsg, wParam, lParam) ;

     }

(프로그램 소스끝)

그림 1은 KeyEx 출력 결과입니다.












     (그림 1) KeyEx 출력 결과



카렛 만들기

카렛이란 윈도우에서 키보드입력시 사용되는 커서를 이야기 합니다. 도스 시절는 글자를 입력할 때 화면에 깜빡이는 직선 바를 커서라고 하였으나 윈도우에서는 카렛이라고 합니다. 윈도우에서 커서는 마우스 커서를 의미하며 워드 프로세서등에 나타나는 키보드 입력시의 커서를 카렛이라고 합니다.

카렛을 만들고 보여주고 그리고 삭제할경우에는  다음과 같은 순서로 됩니다.


//카렛을 만든다.

CreateCaret(hwnd,NULL,xsize,ysize);

//카렛을 보여준다

ShowCaret(hwnd);

//카렛의 위치를 설정한다

SetCaretPos(pos*xsize,0);

   : 문자 입력을 받는동안 카렛의 위치를 바꾸어 준다.

//카렛을 감춘다

HideCaret(hwnd);

//카렛을 삭제한다.

DestroyCaret();


먼저 CreateCaret 함수를 이용하여 카렛을 만듭니다. 첫 번째 인자는 윈도우 핸들이며 두 번째 인자는 HBITMAP 형입니다. 이곳에 비트맵을 설정하면 비트맵 모양의 카렛이 설정됩니다. xsize와 ysize는 카렛의 가로와 세로 크기입니다. 보통 카렛을 만들고자 할 경우 설정된 폰트의 가로세로크기와 같게 만드는 것이 보기에 좋습니다. 그렇기 때문에 xsize와 ysize를 얻기 위해서는 GetTextMetrics 사용하는 것이 좋습니다.


GetTextMetrics(hdc,&tm);

        xsize=tm.tmAveCharWidth;

        ysize=tm.tmHeight;


GetTextMetrics는 GDI편에서 설명하였듯이 문자의 크기 정보를 리턴합니다. 여기에서 tmAveCharWidth 는 문자들의 평균 폭이며 tmHeight는 문자의 높이이므로 이크기에 맞게 카렛을 설정하는 것이 적당합니다.

이렇게 카렛을 만든다음 ShowCaret하시면 카렛이 나타나고 카렛을 특정위치로 이동시키고자 할경우에는 SetCaretPos를 하시면 됩니다.

SetCaretPos는 키보드에서 문자를 입력할 때 계속적으로 이동해야 합니다. 예를들어 "Ab“ 라는 입력이 있었다면 "Ab"다음의 위치로 커서가 이동해야 합니다. 윈도우에서는 고정 폰트를 사용하지않고 가변 폰트를 사용하기 때문에 글자의 폭이 다 다릅니다. 그렇기 때문에 문자의 수를 파악하여 카렛 위치를 이동시키면 커서가 문자열 바로 뒤로 붙지 않습니다. 예를 들어서 "Aj"라는 입력을 했을 경우 평균 폭이 10이라고 가정을 하고 문자열이 두 개이므로 첫 번째 위치부터 20포인트 이후로 이동한다면 "Aj" 보다 더 뒤에 커서가 위치할것입니다.  그이유는 평균 폭이 10이지 'j'같은 글자는 10이하는 작은 폭을 가지고 있기 때문입니다. 카렛 위치를 이동할 때 현재 위치까지의 문자열 전체 폭을 얻고 그위치로 이동하는 것이 좋습니다. 문자열의 폭을 얻는 함수는 GetTextExtentPoint 이며 이함수는 다음과 같습니다.


BOOL GetTextExtentPoint(

HDC hdc, // 디바이스 컨텍스트 핸들

LPCTSTR lpString, // 텍스트 문자열

int cbString, // 텍스트 문자열에서 특정위치까지의 번호

LPSIZE lpSize // 리턴되는 문자열의 가로와 세로 크기

); 


예를 들어 "Abjced" 라는 문자열에서 j라는 문자열까지의 가로와 세로의 크기를 얻고자 한다면 다음과 같이 할수 있습니다.

  SIZE size;

  GetTextExtentPoint(hdc,"Abjced",2,&size);


위와 같이하면 "Abj"의 문자열의 가로크기와 세로크기가 size의 cx와 cy에 저장되게 됩니다. 보통 cy는 크게 필요가 없을것입니다. 문자열의 높이는 가변 폰트라 하더라도 동일하게 간주해도 되기 때문입니다. 그러나 cx의 값은 매우 중요합니다. 이값을 얻어서 SetCaretPos 에 넣어주니까요.

WM_CHAR메세지에 의해서 문자가 들어 올 경우 이문자를 버퍼에 놓고 커서를 문자열 끝으로 이동한다고 할 경우 다음과 같을 것입니다.


  case WM_CHAR:

           //키값을 szBuff에 넣는다

               code=wParam;

               szBuff[pos]=code;

               pos++;

               szBuff[pos]=0x00;

               hdc=GetDC(hwnd);

               //현재 위치의 문자열 크기를 얻는다.

               GetTextExtentPoint(hdc,szBuff,pos,&size);

               //카렛 을 이동시킨다.

               SetCaretPos(size.cx,0);

               ReleaseDC(hwnd,hdc);

               break;


WM_SETFOCUS 와 WM_KILLFOCUS

포커스를 갖는다는 것은 현재 윈도우가 가장 상위에 활성화 되어 있다는 의미이며 입력을 받을수 있다는 의미입니다. 카렛은 입력 포커스를 받는 상황하에서만 활성화 될 수 있습니다. 입력포커스를 받는다는 것을 어떻게 알수가 있을까요. 윈도우가 입력포커스를 받는 상황이 되면 WM_SETFOCUS가 발생되면 포커스가 사라지면 WM_KILLFOCUS가 발생됩니다. 버튼이나 여러 자원들이 설정되어 있지 않는 그냥 윈도우 상태에서는 WM_SETFOCUS는 윈도우가 활성화 될 때 발생되며 윈도우가 다른 윈도우에 의해서 가려지거나 사라지거나 할 경우 WM_KILLFOCUS가 발생됩니다. 다른 윈도우에 의해서 가려져 비활성화 되다가 다시 활성화 되면 또다시 WM_SETFOCUS가 발생됩니다. 카렛을 만들고 화면에 보이고자 할경우에는 WM_SETFOUCUS에 그리고 WM_KILLFOCUS가 발생되면 카렛을 감추고 삭제하는 것이 일반적으로 카렛을 사용하는 방법입니다.

 WM_SETFOCUS :

                카렛을 만들고 화면에 나타냄

               break;

 WM_KILLFOCUS:

               카렛을 감추고 화면에서 삭제한다.

               break;

위의 카렛을 만들고 보여주는 부분은 WM_SETFOCUS에 감추는 부분은 WM_KILLFOCUS에 넣는 것이 이상적일것입니다.

WM_SETFOCUS는 카렛을 만들경우에만 사용되는 것이 아니라 여러개의 차일드 윈도우를 컨트롤 할 때도 사용됩니다. 이부분에 대해서는 뒤의 차일드 윈도우 컨트롤 부분에서 자세히 설명할것입니다.


한줄 문자 입력기 예제 TypingEx

TypingEx는 윈도우 클라이언트 영역(0,0) 좌표에서 총 80개의 문자열을 입력받는 타이핑 예제입니다. 지금까지 설명한 카렛과 WM_SETFOCUS와 WM_KILLFOCUS를 이용하여 1줄짜리의 간단한 에디터를 만든것입니다. 이에디터는 커서가 이동이 되지 않으며 또한 Backspace도 가동되지 않습니다. 다만 입력하는데로 문자가 화면에 출력될것입니다. 이 예제를 통해서 에디터를 어떻게 만드는가에 대한 기본을 알수 있을것입니다.

(프로그램 소스)

//타이핑 예제

//TypingEx.c

#include <windows.h>


void main();


LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;


int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

                    PSTR szCmdLine, int iCmdShow)

     {

     static char szAppName[] = "TypingEx" ;

     HWND        hwnd ;

     MSG         msg ;

     WNDCLASSEX  wndclass ;


     wndclass.cbSize        = sizeof (wndclass) ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;

     wndclass.lpfnWndProc   = WndProc ;

     wndclass.cbClsExtra    = 0 ;

     wndclass.cbWndExtra    = 0 ;

     wndclass.hInstance     = hInstance ;

     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;

     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;

     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;

     wndclass.lpszMenuName  = NULL ;

     wndclass.lpszClassName = szAppName ;

     wndclass.hIconSm       = LoadIcon (NULL, IDI_APPLICATION) ;


     RegisterClassEx (&wndclass) ;


     hwnd = CreateWindow (szAppName,    

                           "타이핑예제:TypingEx",  

                    WS_OVERLAPPEDWINDOW, 

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    NULL,                

                    NULL,                

                    hInstance,           

                           NULL) ;                    


     ShowWindow (hwnd, iCmdShow) ;

     UpdateWindow (hwnd) ;


     while (GetMessage (&msg, NULL, 0, 0))

          {

          TranslateMessage (&msg) ;

          DispatchMessage (&msg) ;

          }

     return msg.wParam ;

     }



LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

     HDC         hdc ;

     PAINTSTRUCT ps ;

        TEXTMETRIC tm;

        static int xsize=0,ysize=0;

        static int pos=0;

        static char szBuff[80];

        SIZE  size;

        char code;

        char temp[80];

        HRGN rgn;

     switch (iMsg)

          {

          case WM_CREATE :

                               strcpy(szBuff,"");

               return 0 ;

                 case WM_SETFOCUS:

                               hdc=GetDC(hwnd);

                               GetTextMetrics(hdc,&tm);

                               xsize=tm.tmAveCharWidth;

                               ysize=tm.tmHeight;

                               //카렛을 만든다.

                               CreateCaret(hwnd,NULL,xsize,ysize);

                               //카렛을 보여준다

                               ShowCaret(hwnd);

                               //카렛의 위치를 설정한다

                               SetCaretPos(pos*xsize,0);

                               ReleaseDC(hwnd,hdc);

                               return 0;

                 case WM_KILLFOCUS:

                           //카렛을 감춘다

                               HideCaret(hwnd);

                               //카렛을 삭제한다.

                               DestroyCaret();

                               return 0;

          case WM_PAINT :

                  hdc = BeginPaint (hwnd, &ps) ;

                          TextOut(hdc,0,0,szBuff,strlen(szBuff));

                  EndPaint (hwnd, &ps) ;

               return 0 ;

                 case WM_CHAR:

                           //키값을 szBuff에 넣는다

                               code=wParam;

                               szBuff[pos]=code;

                               pos++;

                               szBuff[pos]=0x00;

                               //버퍼 크기는 80이므로 80이 넘으로 다시 0

                               if(pos>=80)

                                       pos=0;

                               hdc=GetDC(hwnd);

                               //현재 위치의 문자열 크기를 얻는다.

                               GetTextExtentPoint(hdc,szBuff,pos,&size);

                               //카렛 을 이동시킨다.

                               SetCaretPos(size.cx,0);

                               //문자열 코드를 화면에 출력한다.

                               wsprintf(temp,"char:%03d",wParam);

                               TextOut(hdc,0,20,temp,strlen(temp));

                               ReleaseDC(hwnd,hdc);

                               //무효화 사각형 영역을 만든다.

                               rgn=CreateRectRgn(0,0,500,20);

                               InvalidateRgn(hwnd,rgn,TRUE);

                               DeleteObject(rgn);

                               break;

          case WM_DESTROY :

               PostQuitMessage (0) ;

               return 0 ;

          }


     return DefWindowProc (hwnd, iMsg, wParam, lParam) ;

}

(프로그램 소스끝)

본예제의 출력 결과는 그림 2와 같습니다.













     (그림 2)TypingEx 예제 출력 결과






한글 입출력에 대한 컨트롤

TypingEx를 실행시키면 한글이 완벽하게 완성되었을경우에만 화면에 나타나는 것을 보게 될것입니다. 이것은 조금 불만스러운 느낌을 줍니다. 워드프로세서에서는 한글이 완벽하게 구현이 되지 않더라도 화면에 나타나는데 TypingEx는 그렇게 되지 않으니까요? 실제로 WM_CHAR는 한글이 완벽하게 설정되었을 때만 발생됩니다. 그전에는 발생이 되지 않기 때문에 한글 입력상태에서는 키보드 한문자 한문자에 대해서 WM_CHAR로 알수가 없게 되는 것입니다. 이때는 WM_KEYDOWN으로는 알수가 있으나 문제는 현재가 한글 입력상태인지 또는 영문 입력상태인지를 알수가 없다는 것입니다. 이부분을 해결하기 위해서는 IMM를 핸들링 해야 합니다. IMM이란 Input Method Manager  의 약자입니다. 즉 한글과 같이 여러 키의 조합으로 글자를 만들 경우 사용되는 함수 라이브러리라고 볼수 있습니다. IMM에 대한 여러 함수는 imm.h에 설정되어 있습니다. 본책에서는 IMM에 대한 모든 개념 설명은 하지 않습니다. 단 필요한 부분 즉 현재 입력모드가 한글인가? 영문인가? 와 한글일 경우 완성된 문자가 아니더라도 화면에 출력하는 방법에 대해서 설명을 합니다. 사실 이부분만 알아도 한글을 출력하는데는 문제가 없기 때문입니다.



한영 전환키를 눌렀을때의 메시지

한영 전환키를 눌렀을때는 WM_IME_NOTIFY 발생됩니다. 이때 lParam에는 0값이 wParam에는 6과 8이 교차적으로 나타납니다. 이값으로는 한영전환을 알수가 없습니다. ImmGetConversionStatus 함수를 이용하여 상태를 얻을수는 있습니다. WM_IME_NOTIFY는 한영전환에 대한 여러 Notify를 알려주기 때문에 이때마사 imm.h설정되어 있은 여러 함수를 이용하여 상태를 얻을수 있습니다. 이렇게 복잡한 방법이 아니더라도 한영전환을 알수 있는 또하나의 방법이 있습니다. 그것은 플러그를 만드는것입니다. 프로그램이 시작되어 입력 포커스를 받은 처음상태는 영문 상태입니다. 이때부터 WM_IME_NOTIFY가 발생될때마다 플러그를 변화시키는 것입니다.


//전역 변수로 한영 플러그를 설정하고 초기값을 FALSE로 (영문상태)

BOOL hanFlag=FALSE;


  case WM_IME_NOTIFY:

               hanFlag=1-hanFlag;//한영 플러그 전환

       break;

위와 같이 하면 WM_IME_NOTIFY를 받았을 경우 플러그가 전환됨으로 한글과 영문 상태를 알수가 있습니다. 즉 한영키를 전환하면 1로 설정되면서 한글상태라는 것을 알려주고 다시 한영키를 누르면 0으로 설정되면서 영문상태를 알려주는 것입니다.



한글 조합시작과 종료 메시지

한글 조합의 시작 메시지는 WM_IME_STARTCOMPOSITION 이며 종료 메시지는 WM_IME_ENDCOMPOSITION입니다. 또한 현재 키가 입력되고 조합중이라는 메시지는 WM_IME_COMPOSITION입니다. 이 3개의 메시지를 이용하여 한글을 출력할 수가 있습니다.


                //한글 조합 시작

          case  WM_IME_STARTCOMPOSITION:

                 ://이때는 한글 모드라는 것을 완벽하게 알수 있습니다.

                //한글 조합중

          case WM_IME_COMPOSITION:

                //이때는 현재 한글이 조합중이므로 완성되지 못한 한글 코드를 알수

                // 있습니다.

                        break;

                //한글 조합 완료 즉 영문모드로 전환했을경우

             case WM_IME_ENDCOMPOSITION:

                //이때는 조합이 끝이므로 영문 모드라는 것을 알수 있습니다.

                                break;


WM_IME_STARTCOMPOSITION은 한글 모드가 시작되면서 첫키를 눌렀을 때 발생하는 메시지입니다. 이때는 분명히 한글 모드라는 것을 알수가 있는것입니다.

WM_IME_COMPOSITION은 현재 한글이 조합중이라는 것을 알수 있습니다.

 이때 wParam / 256 이 한글의 상위 비트이며 wParam % 256 한글의 하위비트입니다. 완성이 되지 못한 이글자를 두바이트 이상의 문자열로 만들어 화면에 출력하면 비완성된 문자가 화면에 나타납니다.


                  case WM_IME_COMPOSITION:

                                hdc=GetDC(hwnd);

                                //비완성된 문자를 Han에 받는다

                                Han[0] = (BYTE)(wParam / 256);

                                Han[1] = (BYTE)(wParam % 256);

                                Han[2]=0x00;

                                //화면에 Han를 출력한다.

                                TextOut(hdc,size.cx,0,Han,strlen(Han));

                                ReleaseDC(hwnd,hdc);

                                break;


WM_IME_ENDCOMPOSITION 한글 조합이 끝났다는 의미인데 한영전환키를 눌러서 영문 모드로 전환하였을 경우 발생합니다. 좀더 안전을 기하기 위해서 이때는 한영 전환 플러그 hanFlag를 FALSE로 설정하여 영문모드라는 것을 확인하는 것이 좋을것입니다.


             case WM_IME_ENDCOMPOSITION:

                            hanFlag=FALSE;

                                break;


이렇게 함으로써 한글을 화면에 출력할수 있는 것입니다.


한글출력 1줄 에디터 예제 HanType

HanType는 한글 출력을 하면서 현재 모드가 한글인지 영문인지를 판독할수 있는예제입니다. 이예제는 "imm.h"를 꼭 포함시켜야 합니다.프로그램으 소스는 다음과 같습니다.

(프로그램 소스)

//한글타이핑 예제

//HanType.c

#include <windows.h>

#include "imm.h" //꼭 포함 시킬것




LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;


int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

                    PSTR szCmdLine, int iCmdShow)

     {

     static char szAppName[] = "HanType" ;

     HWND        hwnd ;

     MSG         msg ;

     WNDCLASSEX  wndclass ;


     wndclass.cbSize        = sizeof (wndclass) ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;

     wndclass.lpfnWndProc   = WndProc ;

     wndclass.cbClsExtra    = 0 ;

     wndclass.cbWndExtra    = 0 ;

     wndclass.hInstance     = hInstance ;

     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;

     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;

     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;

     wndclass.lpszMenuName  = NULL ;

     wndclass.lpszClassName = szAppName ;

     wndclass.hIconSm       = LoadIcon (NULL, IDI_APPLICATION) ;


     RegisterClassEx (&wndclass) ;


     hwnd = CreateWindow (szAppName,    

                           "타이핑예제:Hantype",  

                    WS_OVERLAPPEDWINDOW, 

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    NULL,                

                    NULL,                

                    hInstance,           

                           NULL) ;                    


     ShowWindow (hwnd, iCmdShow) ;

     UpdateWindow (hwnd) ;


     while (GetMessage (&msg, NULL, 0, 0))

          {

          TranslateMessage (&msg) ;

          DispatchMessage (&msg) ;

          }

     return msg.wParam ;

     }

//전역 변수로 한영 플러그를 설정하고 초기값을 FALSE로 (영문상태)

BOOL hanFlag=FALSE;


LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

     HDC         hdc ;

     PAINTSTRUCT ps ;

        TEXTMETRIC tm;

        static int xsize=0,ysize=0;

        static int pos=0;

        static char szBuff[80];

        SIZE  size;

        char code;

        char temp[80];

        char Han[5];

        HRGN rgn;

     switch (iMsg)

          {

          case WM_CREATE :

                               strcpy(szBuff,"");

               return 0 ;

                 case WM_SETFOCUS:

                               hdc=GetDC(hwnd);

                               GetTextMetrics(hdc,&tm);

                               xsize=tm.tmAveCharWidth;

                               ysize=tm.tmHeight;

                               CreateCaret(hwnd,NULL,xsize,ysize);

                               ShowCaret(hwnd);

                               SetCaretPos(pos*xsize,0);

                               ReleaseDC(hwnd,hdc);

                               return 0;

                 case WM_KILLFOCUS:

                               HideCaret(hwnd);

                               DestroyCaret();

                               return 0;

          case WM_PAINT :

                  hdc = BeginPaint (hwnd, &ps) ;

                          TextOut(hdc,0,0,szBuff,strlen(szBuff));

                  EndPaint (hwnd, &ps) ;

               return 0 ;

                 case WM_CHAR:

                               code=wParam;

                               szBuff[pos]=code;

                               pos++;

                               szBuff[pos]=0x00;

                               if(pos>=80)

                                       pos=0;

                               hdc=GetDC(hwnd);

                               GetTextExtentPoint(hdc,szBuff,pos,&size);

                               SetCaretPos(size.cx,0);

                               wsprintf(temp,"char:%03d",wParam);

                               TextOut(hdc,0,20,temp,strlen(temp));

                               ReleaseDC(hwnd,hdc);

                               rgn=CreateRectRgn(0,0,500,20);

                               InvalidateRgn(hwnd,rgn,TRUE);

                               DeleteObject(rgn);

                               break;

                               //한글 조합 시작

                 case  WM_IME_STARTCOMPOSITION:

                               wsprintf(temp,"조합상태");

                               hdc=GetDC(hwnd);

                               TextOut(hdc,0,60,temp,strlen(temp));

                               ReleaseDC(hwnd,hdc);

                               break;

                               //한글 조합중

                 case WM_IME_COMPOSITION:

                               hdc=GetDC(hwnd);

                               GetTextExtentPoint(hdc,szBuff,pos,&size);

                               //비완성된 문자를 Han에 받는다

                               Han[0] = (BYTE)(wParam / 256);

                               Han[1] = (BYTE)(wParam % 256);

                               Han[2]=0x00;

                               //화면에 Han를 출력한다.

                               TextOut(hdc,size.cx,0,Han,strlen(Han));

                               ReleaseDC(hwnd,hdc);

                               break;

                               //한글 조합 완료 즉 영문모드로 전환했을경우

             case WM_IME_ENDCOMPOSITION:

                           hanFlag=FALSE;

                               wsprintf(temp,"영문");

                               hdc=GetDC(hwnd);

                               TextOut(hdc,0,40,temp,strlen(temp));

                               ReleaseDC(hwnd,hdc);

                               break;

                 case WM_IME_NOTIFY:

                               hanFlag=1-hanFlag;//한영 플러그 전환

                               if(hanFlag)

                               {

                                       wsprintf(temp,"한글");

                                       hdc=GetDC(hwnd);

                                       TextOut(hdc,0,40,temp,strlen(temp));

                                       ReleaseDC(hwnd,hdc);

                               }

                               break;

          case WM_DESTROY :

               PostQuitMessage (0) ;

               return 0 ;

          }


     return DefWindowProc (hwnd, iMsg, wParam, lParam) ;

}

(프로그램 소스끝)

그림 3은 HanType 출력 결과입니다.









       (그림 3)HanType 출력 결과


한글 키 컨트롤 및 상태 얻기

HanType 예제에서는 완벽하게 한글 전환이 되지 않습니다. 입력을 하다가 space바를 누르게 되면 갑자기 영문상태로 바뀌거나 잠시 잠깐 한글과 영문상태가 잘못 출력되는 오류가 나오게 됩니다. 이제 완벽하게 한글입출력을 할수 있는 방법을 알아보겠습니다.

첫 번째로  현재 상태가 한글인지 영문인지를 알고자 할 경우 이때는 다음과 같은 방법을 이용합니다.

HIMC data; //IMM핸들 변수 설정


//한글인지 영문인지를 알아본다

//현재 윈도우의 IMM 핸들러을 얻는다.

data=ImmGetContext(hwnd);

//한영상태를 얻는다.

//hanFlag가 1이면 한글 0이면 영문임

 hanFlag=ImmGetOpenStatus(data);

//IMM핸들을 해제 한다.

ImmReleaseContext(hwnd,data);


ImmGetContext 함수를 이용하여 IMM핸들러를 얻은다음 ImmGetOpenStatus함수를 이용하여 현재 입력모드를 알아내면 됩니다. 이때 리턴되는 값이 1이면 한글이고 0이면 영문으로 설정된것입니다. ImmGetContext와 쌍인 ImmReleaseContext함수를 이용하여 IMM 핸들을 해제시켜 주어야 합니다.


두 번째로 우리가 한영전환을 마음대로 하고자 할경우가 있습니다. 이방법은  ImmSetConversionStatus 함수를 이용하면 됩니다.


        //핸들러 얻기

        data=ImmGetContext(hwnd);

        //한글전환시

        ImmSetConversionStatus(data,1,0);

        //영문 전환시

        ImmSetConversionStatus(data,0,0);

        //핸들러 해제


ImmSetConversionStatus함수의 두 번째 인자값을 1로 하였을 경우 한글로 설정되고 0으로 하였을 경우 1로 설정됩니다. 많일 프로그램이 시작되면서 바로 한글모드로 하고 싶다면 ImmSetConversionStatus함수의 두 번째 인자를 1로 설정하고 실행시키면 됩니다.



한글전환 예제  HanType2

HanType2는 좌측 마우스 버튼을 클릭하면 토글로 한영이 전환되며 완벽하게 현재의 한영 상태를 알아내는 1줄 한글 입력 에디터 예제입니다.

이예제에서는 imm32.lib 라이브러리가 필요합니다. 프로젝트에서 Alt+F7키를 누르거나 Project메뉴에서  Settings항목을 선택하고 카드철에서 Link를 선택하고 그림 4와 같은 화면이 나올 때 Object/library modules: 항목에 imm32.lib를 입력하여 주셔야 합니다.














          (그림 4) Settings 화면


프로그램 소스는 다음과 같습니다.

(프로그램 소스)

//한글타이핑 예제

//HanType.c

#include <windows.h>

#include "imm.h"




LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;


int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

                    PSTR szCmdLine, int iCmdShow)

     {

     static char szAppName[] = "HanType" ;

     HWND        hwnd ;

     MSG         msg ;

     WNDCLASSEX  wndclass ;


     wndclass.cbSize        = sizeof (wndclass) ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;

     wndclass.lpfnWndProc   = WndProc ;

     wndclass.cbClsExtra    = 0 ;

     wndclass.cbWndExtra    = 0 ;

     wndclass.hInstance     = hInstance ;

     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;

     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;

     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;

     wndclass.lpszMenuName  = NULL ;

     wndclass.lpszClassName = szAppName ;

     wndclass.hIconSm       = LoadIcon (NULL, IDI_APPLICATION) ;


     RegisterClassEx (&wndclass) ;


     hwnd = CreateWindow (szAppName,    

                           "타이핑예제:HanType",  

                    WS_OVERLAPPEDWINDOW, 

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    NULL,                

                    NULL,                

                    hInstance,           

                           NULL) ;                    


     ShowWindow (hwnd, iCmdShow) ;

     UpdateWindow (hwnd) ;


     while (GetMessage (&msg, NULL, 0, 0))

          {

          TranslateMessage (&msg) ;

          DispatchMessage (&msg) ;

          }

     return msg.wParam ;

     }

//전역 변수로 한영 플러그를 설정하고 초기값을 FALSE로 (영문상태)

BOOL hanFlag=FALSE;

HIMC data;


LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

     HDC         hdc ;

     PAINTSTRUCT ps ;

        TEXTMETRIC tm;

        static int xsize=0,ysize=0;

        static int pos=0;

        static char szBuff[80];

        SIZE  size;

        char code;

        char temp[80];

        char Han[5];

        HRGN rgn;

     switch (iMsg)

          {

          case WM_CREATE :

                               strcpy(szBuff,"");

               return 0 ;

                 case WM_SETFOCUS:

                               hdc=GetDC(hwnd);

                               GetTextMetrics(hdc,&tm);

                               xsize=tm.tmAveCharWidth;

                               ysize=tm.tmHeight;

                               CreateCaret(hwnd,NULL,xsize,ysize);

                               ShowCaret(hwnd);

                               SetCaretPos(pos*xsize,0);

                               ReleaseDC(hwnd,hdc);

                               return 0;

                 case WM_KILLFOCUS:

                               HideCaret(hwnd);

                               DestroyCaret();

                               return 0;

          case WM_PAINT :

                  hdc = BeginPaint (hwnd, &ps) ;

                          TextOut(hdc,0,0,szBuff,strlen(szBuff));

                  EndPaint (hwnd, &ps) ;

               return 0 ;

                 case WM_CHAR:

                               code=wParam;

                               szBuff[pos]=code;

                               pos++;

                               szBuff[pos]=0x00;

                               if(pos>=80)

                                       pos=0;

                               hdc=GetDC(hwnd);

                               GetTextExtentPoint(hdc,szBuff,pos,&size);

                               SetCaretPos(size.cx,0);

                               wsprintf(temp,"char:%03d",wParam);

                               TextOut(hdc,0,20,temp,strlen(temp));

                               ReleaseDC(hwnd,hdc);

                               rgn=CreateRectRgn(0,0,500,20);

                               InvalidateRgn(hwnd,rgn,TRUE);

                               DeleteObject(rgn);

                               break;

                 case WM_KEYDOWN:

                               //한글인지 영문인지를 알아본다

                           //현재 윈도우의 IMM 핸들러을 얻는다.

                               data=ImmGetContext(hwnd);

                               //한영상태를 얻는다.

                               //hanFlag가 1이면 한글 0이면 영문임

                           hanFlag=ImmGetOpenStatus(data);

                               //IMM핸들을 해제 한다.

                               ImmReleaseContext(hwnd,data);

                               if(hanFlag)

                               {

                                       wsprintf(temp,"한글");

                                       hdc=GetDC(hwnd);

                                       TextOut(hdc,0,40,temp,strlen(temp));

                                       ReleaseDC(hwnd,hdc);

                               }

                               else

                               {

                                       wsprintf(temp,"영문");

                                       hdc=GetDC(hwnd);

                                       TextOut(hdc,0,40,temp,strlen(temp));

                                       ReleaseDC(hwnd,hdc);

                               }


                               break;

                               //한글 조합 시작

                 case  WM_IME_STARTCOMPOSITION:

                               wsprintf(temp,"조합상태");

                               hdc=GetDC(hwnd);

                               TextOut(hdc,0,60,temp,strlen(temp));

                               ReleaseDC(hwnd,hdc);

                               break;

                               //한글 조합중

                 case WM_IME_COMPOSITION:

                               hdc=GetDC(hwnd);

                               GetTextExtentPoint(hdc,szBuff,pos,&size);

                               //비완성된 문자를 Han에 받는다

                               Han[0] = (BYTE)(wParam / 256);

                               Han[1] = (BYTE)(wParam % 256);

                               Han[2]=0x00;

                               //화면에 Han를 출력한다.

                               TextOut(hdc,size.cx,0,Han,strlen(Han));

                               ReleaseDC(hwnd,hdc);

                               break;

                               //한글 조합 완료 즉 영문모드로 전환했을경우

             case WM_IME_ENDCOMPOSITION:

                               wsprintf(temp,"영문");

                               hdc=GetDC(hwnd);

                               TextOut(hdc,0,40,temp,strlen(temp));

                               ReleaseDC(hwnd,hdc);

                               break;

                 case WM_LBUTTONDOWN:

                               hanFlag=1-hanFlag;//한영 플러그 전환

                               //핸들러 얻기

                               data=ImmGetContext(hwnd);

                               //한영 전환을 한다.

                               ImmSetConversionStatus(data,hanFlag,0);

                               //핸들러 해제

                               ImmReleaseContext(hwnd,data);

                               if(hanFlag)

                               {

                                       wsprintf(temp,"한글");

                                       hdc=GetDC(hwnd);

                                       TextOut(hdc,0,40,temp,strlen(temp));

                                       ReleaseDC(hwnd,hdc);

                               }

                               else

                               {

                                       wsprintf(temp,"영문");

                                       hdc=GetDC(hwnd);

                                       TextOut(hdc,0,40,temp,strlen(temp));

                                       ReleaseDC(hwnd,hdc);

                               }

                               break;

          case WM_DESTROY :

               PostQuitMessage (0) ;

               return 0 ;

          }


     return DefWindowProc (hwnd, iMsg, wParam, lParam) ;

}


(프로그램 소스끝)

그림 5는 HanType2 의 출력 결과입니다.














         (그림 5) HanType2출력 결과



(잔소리)

지금까지 C언어를 배우고 윈도우에서 기본적인 내용을 공부하셨습니다. 혹이 근심된 마음에서 말씀드리는데 지금까지 공부한내용이 조금은 알겠는데 확실히 모르겠다 하시는 분들이 계신다면 주저 없이 다음으로 넘어가주시기를 바랍니다. “에이 처음부터 다시 확실하게 공부하고 넘어가자” 라고 다시 처음으로 가지 말아주시기 바랍니다. 컴퓨터 프로그램은 절대 줄그어 가면서 한 Chapter 한 Chapter이해해가면서 완벽을 기하는 방법으로 공부하시면 절대 안됩니다. 그냥 페인트 칠하는 기법으로 한번 쫘악 보시고 다시 필요한 부분을 또한 번 보고 이렇게 함으로써 이해가 되는 것입니다. 지금까지 공부하실 때 “어떤 내용은 어느 Chapter에 있더라” 이것만 알아도 됩니다. 이렇게 알면서 뒤의것을 공부하시면 앞에 것을 또한번 이해할수 있습니다. 그리고 책을 다보신 후에(3편까지만 보아도 됩니다) 자신이 한번 프로그램을 만들어 보세요. 그리고 막히는 부분이 있다면 “아! 책 어느부분에 있구나” 하시고 그부분을 다시 보시던지 그래도 모르면 그냥 예제를 부분 카피 하셔서 사용해도 됩니다.  윈도우 프로그래밍은 사실 언어만 이해하면 이론적으로 공부하는 부분은 작고 그냥 시키는대로 하는게 더많습니다. 즉 어떻게 해야 하는가가 많다는 이야기입니다. 그것은 기술력에 해당되지 않습니다. 노우하우 (어떤면에서는 노우웨어 (Know Where)에 해당되는 것입니다. 앞으로 본책의 뒤로가면서 이론적 부분이 점점 사라지고 “이렇게 하면 됩니다” 라는 말이 많이 나올것입니다. 이게 사실이기 때문입니다. 왜냐면 마이크로 소프트사에서 주는 라이브러리를 사용하여 프로그램을 짜기 때문에 그들이 제공하는 라이브러리 함수를 순서에 맞게 설정하는 것 외에는 그렇다할 알고리듬이 없기 때문입니다. SDK라이브러리에 어떤 기술력이 있다고 괸히 어려운 함수 쓸려고 노력하시는 분들에게 한마디 하고 싶습니다. “그시간에 알고리듬 공부를 더하세요!”

(잔소리끝)

 

 

 

 

 

 

 

<자료출저 : 삼육대학교 이상엽 박사>

반응형
반응형

http://blog.naver.com/icebear2304/140133351366


Buttons



CMFCRibbonPanel* pPanel3 = pCategory->AddPanel(_T("Check Boxes"));


pPanel3->Add(new CMFCRibbonCheckBox(ID_RIBBON_BTN_7, _T("Check Box 1")));

pPanel3->Add(new CMFCRibbonCheckBox(ID_RIBBON_BTN_8, _T("Check Box 2")));

pPanel3->Add(new CMFCRibbonCheckBox(ID_RIBBON_BTN_9, _T("Check Box 3")));


Palette Buttons



Color Buttons





Groups



Edit and Combo




Other






반응형
반응형

http://ntcoder.com/bab/mfc-feature-pack-tutorial-part-3-cmfcpropertygridctrl/

MFC Feature Pack Tutorial – Part 3 – CMFCPropertyGridCtrl

 

It’s a cool new control found in MFC feature pack. Those who’ve used VB, C#, must be familiar with this control. It’s the good old vb property control. This is how the old vb control looked.

It can be called a two column list control with a tree embedded inside it. But it is not just that, here are some properties of this new control…

  1. Allows custom properties
  2. Allows custom controls inside property values
  3. Allows nested properties
  4. Allows font properties
  5. Allows color properties
  6. Fires events when a property is updated
  7. Allows grouping of properties
  8. VS look and feel
  9. Allows custom colors
  10. Allows feedback for properties that has changed, changes text style to bold
  11. One touch expand of all nodes
  12. Allows a toolbar to be placed at the top
  13. Allows boolean properties

So what’s the class name of this new control?

Of course it’s CMFCPropertyGridCtrl.

Basic architecture

Base class of all properties in this control is called CMFCPropertyGridProperty. Derive from this class to add on more properties. Each CMFCPropertyGridProperty can take additional sub properties which in turn again can take further sub properties, which in turn can take further sub properties… So kinda makes a tree structure.

All items get removed in OnDestroy of this control. Every property get’s “delete”d which turn callsCMFCPropertyGridProperty’s destructor which in turn calls child properties destructor which again in turn calls any futher child properties destructor and this goes on for every property in the control.

All properties must be allocated on the heap using “new”. An exception to this rule are nested child properties of CMFCPropertyGridProperty which can be on the stack but make sure that you remove them before the call to OnDestroy of this control, since delete get’s applied to every property and it’s sub property. Also make sure that such an object has sufficient life time. There is an option in CMFCPropertyGridProperty::RemoveSubItemto prevent “delete”ing of such properties.

Adding properties

To add a property to this control we use the function called CMFCPropertyGridCtrl::AddProperty, note that properties should be dynamically allocated, because CMFCPropGridCtrl::OnDestroy uses delete to free properties.

Deleting properties

To remove a property we use the function called CMFCPropertyGridCtrl::DeleteProperty. Note what was said earlier, all properties should be dynamically allocated. Also note that only properties that was directly given to this control will be deleted, sub properties should be deleted via their parent property class only.

Adding sub properties

To add a sub property call CMFCPropertyGridProperty::AddSubItem with the address of the new sub item. It’s up to you to decide whether this sub property should be on the heap or on the stack. If it’s on the stack then make sure you remove such an item before the call to main property grid control’s OnDestroy.

Removing sub properties

All sub properties get removed when the control is destroyed but such properties should be allocated on the heap.

To remove sub properties in between call CMFCPropertyGridProperty::RemoveSubItem. It takes a pointer reference to the item that is to be deleted and optional last boolean parameter which if set will delete given pointer, else won’t.

Show us some code man!

Ok don’t loose your cool, enough of preaching now it’s all practical stuff mate. :P

Here is a function which sets up a simple property grid control. With some properties and some nested properties.

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
void CPropertyGridCtrlTestDlg::InitPropGrid()
{
    // Switch look and feel to office 2007 style
    CMFCVisualManagerOffice2007::SetStyle( CMFCVisualManagerOffice2007::Office2007_ObsidianBlack );
    CMFCVisualManager::SetDefaultManager( RUNTIME_CLASS( CMFCVisualManagerOffice2007 ));
 
    CRect Rect;
    m_wndPropListLocation.GetClientRect( Rect );
    m_wndPropListLocation.MapWindowPoints( this, &Rect );
 
    m_wndPropList.Create( WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP, Rect, this, 1231 );
    m_wndPropList.EnableHeaderCtrl( TRUE, _T( "Nibu's Property" ), _T( "Nibu's Value" ));
    m_wndPropList.SetVSDotNetLook( TRUE );
 
    // Create a property group for appearance
    CMFCPropertyGridProperty * pGroupTest = new CMFCPropertyGridProperty( _T( "Group Test" ) );
    m_wndPropList.AddProperty( pGroupTest );
 
    const int MaxNesting = 5;
    CMFCPropertyGridProperty *pParent = pGroupTest;
    for( int Index = 0; Index < MaxNesting; ++Index )
    {
        CString Text;
        Text.Format( _T( "Nesting %d" ), Index + 1 );
 
        CMFCPropertyGridProperty* pParentTemp = new CMFCPropertyGridProperty( Text );
 
        // Display's a combo with options as True, False, Cool!
        COleVariant Bool((short)VARIANT_FALSE, VT_BOOL);
        pParent->AddSubItem(new CMFCPropertyGridProperty(_T("Bool test"), Bool, _T("Testing kids")));
        pParent->AddSubItem( pParentTemp );
        pParent = pParentTemp;
    }
 
    // A font property
    LOGFONT lf = { 0 };
    GetFont()->GetLogFont( &lf );
    CMFCPropertyGridFontProperty* pFntProp = new CMFCPropertyGridFontProperty( _T( "Font (Font dialog comes up)" ), lf );
    pGroupTest->AddSubItem( pFntProp );
 
    // Combo property, set sub options which are displayed in a combo
    CMFCPropertyGridProperty* pCmbProp = new CMFCPropertyGridProperty(_T("Border (A combo box)"), _T("Dialog Frame"), _T("One of: None, Thin, Resizable, or Dialog Frame"));
    pCmbProp->AddOption(_T("None"));
    pCmbProp->AddOption(_T("Thin"));
    pCmbProp->AddOption(_T("Resizable"));
    pCmbProp->AddOption(_T("Dialog Frame"));
    pCmbProp->AllowEdit(FALSE);
    pGroupTest->AddSubItem( pCmbProp );
 
    // A folder browse dialog property
    CMFCPropertyGridFileProperty* pFolderProp = new CMFCPropertyGridFileProperty( _T( "Select folder (Browse for folder dialog)" ), _T( "C:\\Windows" ));
    pGroupTest->AddSubItem( pFolderProp );
 
    // A file open dialog property
    CMFCPropertyGridFileProperty* pFileProp = new CMFCPropertyGridFileProperty( _T( "Select file (Open file dialog)" ), TRUE, _T( "C:\\Windows" ));
    pGroupTest->AddSubItem( pFileProp );
 
    // A masked edit control for phone number
    pGroupTest->AddSubItem( new CMFCPropertyGridProperty(_T("Phone (Masked edit)"), _T("(123) 123-12-12"), _T("Enter a phone number"), 0, _T(" ddd  ddd dd dd"), _T("(___) ___-__-__")));
 
    // A color property
    CMFCPropertyGridColorProperty* pColorProp = new CMFCPropertyGridColorProperty( _T( "Select color" ), RGB( 120, 198, 250 ));
    pGroupTest->AddSubItem( pColorProp );
 
    // Set custom colors for property grid
    m_wndPropList.SetCustomColors(RGB(228, 243, 254), RGB(46, 70, 165), RGB(200, 236, 209), RGB(33, 102, 49), RGB(255, 229, 216), RGB(128, 0, 0), RGB(159, 159, 255));
}

And the output looks like this…

반응형
반응형

BLOG main image




아래처럼 프로퍼티를 입력한 후..



CMFCPropertyGridProperty* pGroup=NULL;



pGroup = new CMFCPropertyGridProperty(_T("tests"));



pGroup->AddSubItem( new CMFCPropertyGridProperty( ..... )  );

pGroup->AddSubItem( new CMFCPropertyGridProperty( ..... )  );

pGroup->AddSubItem( new CMFCPropertyGridProperty( ..... )  );

pGroup->AddSubItem( new CMFCPropertyGridProperty( ..... )  );

pGroup->AddSubItem( new CMFCPropertyGridProperty( ..... )  );

.....



디버깅해서 보니 값이 CMFCPropertyGridProperty의 서브아이템으로 들어가 있는 것을 알수 있었다






_view->_propertyGridLeft.AddProperty(pGroup); //프로퍼티 추가

CMFCPropertyGridProperty* pGroup_0=_view->_propertyGridLeft.GetProperty(0);                     

// _propertyGridLeft : 프로퍼티전역 변수에서 인덱스로 해당 프로퍼티를 얻어온다



CMFCPropertyGridProperty* subGrpup_0 = pGroup_0->GetSubItem(0);    얻어온 프로퍼티의 서브아이템의 순서

CMFCPropertyGridProperty* subGrpup_5 = pGroup_0->GetSubItem(5);    에 대해서 이처럼 해당 값을 얻을 수 있는 서브프로퍼티를 얻을 수있고


COleVariant vars= subGrpup_0->GetValue();                                        우선 이렇게 얻어온 후

COleVariant vars5= subGrpup_5->GetValue();



입력했던 값 형태에 따라 변수 형태만 지정해서 읽어오면 된다


float varf =  vars.fltVal;                                                                           //0번째 서브아이템에 입력했던 값은 float 였고

wchar_t* strs =  vars5.bstrVal;                                                                //5번째 서브아이템에 입펵했떤 값은 wchar_t 였다







변수 값은 oaidl.h 헤더파일중




struct tagVARIANT

    {

    union 

        {

        struct __tagVARIANT

            {

            VARTYPE vt;

            WORD wReserved1;

            WORD wReserved2;

            WORD wReserved3;

            union 

                {

                LONGLONG llVal;

                LONG lVal;

                BYTE bVal;

                SHORT iVal;

                FLOAT fltVal;

                DOUBLE dblVal;

                VARIANT_BOOL boolVal;

                _VARIANT_BOOL bool;

                SCODE scode;

                CY cyVal;

                DATE date;

                BSTR bstrVal;

                IUnknown *punkVal;

                IDispatch *pdispVal;

                SAFEARRAY *parray;

                BYTE *pbVal;

                SHORT *piVal;

                LONG *plVal;

                LONGLONG *pllVal;

                FLOAT *pfltVal;

                DOUBLE *pdblVal;

                VARIANT_BOOL *pboolVal;

                _VARIANT_BOOL *pbool;

                SCODE *pscode;

                CY *pcyVal;

                DATE *pdate;

                BSTR *pbstrVal;

                IUnknown **ppunkVal;

                IDispatch **ppdispVal;

                SAFEARRAY **pparray;

                VARIANT *pvarVal;

                PVOID byref;

                CHAR cVal;

                USHORT uiVal;

                ULONG ulVal;

                ULONGLONG ullVal;

                INT intVal;

                UINT uintVal;

                DECIMAL *pdecVal;

                CHAR *pcVal;

                USHORT *puiVal;

                ULONG *pulVal;

                ULONGLONG *pullVal;

                INT *pintVal;

                UINT *puintVal;

                struct __tagBRECORD

                    {

                    PVOID pvRecord;

                    IRecordInfo *pRecInfo;

                    } __VARIANT_NAME_4;

                } __VARIANT_NAME_3;

            } __VARIANT_NAME_2;

        DECIMAL decVal;

        } __VARIANT_NAME_1;

    } ;

typedef VARIANT *LPVARIANT;





구조체에서 찾아볼 수 있으며 원하는 형식의 변수로 읽어오면 된다





이것에 대해 입력 메세지( API 의 입력키 메세지  )처럼 처리하기도 한다는 것을 검색하다 알게 됐다[링크 걸어놓음]



http://knob.egloos.com/2785616  






반응형
반응형

boolean 변수 추가

COleVariant boolVar((short)VARIANT_FALSE, VT_BOOL);

group->AddSubItem(new CMFCPropertyGridProperty(_T("출력1"), boolVar, _T("출력2")));







http://ingorae.tistory.com/260


프로퍼티그리드 컨트롤 팁 하나. CMFCPropertyGridProperty 아이템에서 숫자만 입력 받는 방법은 정수 변수를 선언해놓고 CMFCPropertyGridProperty 인자로 넣어주면 된다. 이러면 문자가 입력안된다. 반대로 문자로 쓸때는 _T("문자열") 파라미터가 들어가겠지. 간만에 보니 생각이 안나서 적어 본다. 아래는 샘플.

long nTestNum = 0;
CMFCPropertyGridProperty* pProp = new CMFCPropertyGridProperty(_T("타이틀"), nTestNum, _T("설명"));







반응형
반응형


http://blog.daum.net/aonezip/12


VS2010 VC++ 살펴보기 01 : CMFCXXXX 시리즈 컨트롤

 

 

 일단 VS2010의 IDE의 외형은 매우 세련된 느낌을 준다. 선택된 문서도 황금색(개인적으로 난 황금색을 무척 좋아한다.)으로 표현해주고, 전체적인 IDE 디자인 자체가 VS2008에 비해서 크게 변하진 않았지만, 미묘하게 훨씬 세련된 느낌을 주는 IDE이다. 조금 사용한 느낌으로는 회사 프로젝트를 2010으로 변환해서 작업하고 싶은 충돌을 들게 만든다. ㅡ.ㅡ;;; 미묘하게 멋지다.

 

 

 IDE의 새로운 기능들은 이런저런 테스트하면서 하나 하나 알아가야 겠다. 일단 하나를 소개 시켜준다면, 현 프로젝트에서 열린 문서를 따로 떼서 하나의 윈도우로 볼수 있다. 이 기능을 소스 화면 분활 기능을 대처 해줄 뿐만 아니라, 아주 유용하게 사용할 수 있다. 이제 더 이상 자주 보는 문서를 alt+W+W으로 찾을 필요가 없어졌고, Ctrl + Tab으로 볼 필요가 없어졌다. 자주 보는 헤더 파일은 항상 창을 따로 떼서 볼 수 있게 되었다.

 

 

[이렇게 자신이 원하는 소스 파일을 따로 떼서 볼수 있습니다. 굿!뚜!]

 

 

 일단은 VS2010에서 VC++의 MFC에서 변화된 부분에 대해서 테스트 해보려고 한다. 물론 실무에서 바로 사용해야하는 추가된 컨트롤들을 살펴볼 예정이다. 그 외에 세부적인 사항에 대해서 좀더 알아보고. 이후에 C++0x에 대해서 알아봐야 할 것 같다. 일단, 새로 추가된 컨트롤들이 어떤 기능을 갖고 어느정도 사용가능 할지 함 알아볼 생각이다. MFC로 게임 Tool를 만들 때 도움이 많이 되는 것들이 추가되었으면 하는 바램인데.. 어떨지 모르겠다.

 

 일단 MFC로 Dialog베이스로 생성하면 기존의 HTML로 만들어지는 Dialog가 그디어 디폴트에서 빠져 있음을 확인할수 있다. 2008에서는 항상 기본으로 되어 있어서 잠시 깜박하면 HTML형으로 생성되서 짜증났는 데… 나만 그런게 아니였나 보다. 정말 급할 때 잘못 생성하면 정말 짜증난다.

 

 

 

VC++ 에피소드 6 - 클래스 위자드의 귀환 (XXXX) SF, 모험, 액션

 

 그래.. 그디어 VC++의 클래스 위자드가 돌아왔다. 도대체 왜 이런 녀석을 .Net이 나오면서 빠져버렸는지.. 당췌 이해가 되지 않지만, 여하튼 다시 2010에서 우리의 품으로 돌아왔다. 일단 겉 모습 및 형태는 기존과 동일하다. 약간 더 이뻐진 것 같기도 하지만, 이제 더 이상 메시지나 이벤트 처리를 위해서 Property창을 볼 필요가 없어졌다. 물론 기존에 지원했던 property로 이벤트나 메시지를 추가하는 interface는 그대로 존재한다. .Net부터 이용했던 프로그래머라면 아마도 이쪽이 더 편리할지도 모르겠다. 2가지Interface를 모두 지원하는 것은 탁월한 선택이 아닌가 싶다.

 

 

 

 [클래스 위자드] 그디어 돌아왔다. 6.0사용자라면 이제 .Net로 MFC를 만들어 되지 않을까?

 

 그런 이 녀석의 단축키가 변했다. 내 기억이 맞다면 물론 오래전 일이지만 단축키는 분명 Ctrl+W였을 텐데.. 이번에 기본 단축키는Shift+Ctrl+X라는 기가막히게 누르기 힘든 단축키로 되어있다. 대체 이렇게 기본 단축키를 할당한 이유가 뭔지 정말 궁금하다. 이보다 더 중요한게 뭔데.. 이렇게 단축키가 변화된것일까? 어이 없게도, 마우스 더블클릭이나 Ctrl+마우크 클릭과 같은 문자 Select기능이다. ㅡ_ㅡ’

 

여하튼 이 클래스 위자드의 귀환을 내 Wnamp에서 Maroon5 – Harder to Breathe로 축해주고 있다.

 

 

 

새로 추가된 MFC Control

 

 이번에 추가된 컨트롤 앞에는 MFC라고 붙는다. 추가된 컨트롤은 VS2008이후에 총 11종으로 아래 그림과 같다.

 

 

 

 이름에서도 알수 있듯이, 컨트롤 이름으로 대략적인 컨트롤의 능력을 알수 있다. 그리고 컨트롤과 Class도 기억하기 쉽게 되어있다. MFC Link Control의 Class명은 CMFCLinkCtrl이다. 마지막 Control만 약어로 쓰면 된다. 이번에 추가된 컨트롤들은 Tool를 만들 때 꽤나 유용하게 사용될 녀석들이 많다. 물론 이 컨트롤로 상용 Application을 만들거라면 비추겠지만, 개발에서 사용될 Tool를 만든다면 꽤나 든든한 녀석들이 될 것이다.

 

CMFCLinkCtrl, CMFCShellTreeCtrl, CMFCShellListCtrl, CMFCPropertyGridCtrl를 이단 살펴봤는 데.. 상당히 쓸만한 녀석들이다.

 

 

 

 

CMFCLinkCtrl

 

 간단하게 인터넷 링크를 걸수 있다. 링크를 클릭하면 해당 Shell에 연결된 프로그램이 자동으로 실행되거나, 기본 브라우져로 해당 주소로 이동한다.

 

 

 

l  인터넷 주소를 쓰면 익스플러우가 실행되면 해당 사이트로 이동한다.

l  C:\\와 같이 폴더명을 쓰면 해당 폴더가 열린다.

l  C:\test.txt 와 같이 파일명을 주면 해당 파일에 연결된 프로그램이 자동으로 실행된다.

 

사용법은 무척이나 간당하다. 해당 Ctrl의 Property에서 설정값을 셋팅할 수 있으며 코드로는 아래와같이 변환해서 사용하면 된다.해당 기능은 Tool에서 Tool과 관련된 기획 문서의 Document를 연결해 놓고 언제든지 툴과 함께 볼수 있도록 하는 기능으로 사용하면 유용할 것 같다. 간편하면서 쉽게 추가 할 수 있다. 

 

1
2
3
4
5
6
7
8
void CMy2010ControlTestDlg::OnBnClickedButton1()
{
    // TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
    m_cTestLinkCtrl.SetURL(L"c:\\");
    m_cTestLinkCtrl.SetTooltip(L"C Drive");
    m_cTestLinkCtrl.SetWindowTextW(L"C 드라이브로 이동");
    m_cTestLinkCtrl.SizeToContent();
}

 

 

CMFCLinkCtrl::SetURL

 Displays a specified URL as the button text.

 

CMFCLinkCtrl::SetURLPrefix

 Sets the implicit protocol (for example, "http:") of the URL.

 

CMFCLinkCtrl::SizeToContent

 Resizes the button to contain the button text or bitmap.

 

http://msdn.microsoft.com/ko-kr/library/bb983604.aspx

 

 

 

CMFCShellTreeCtrl

 

 나 같은 인간에게 있어서 정말 필요한 컨트롤이라고 해야겠다. 간단한 쉘 기능이 들어간 Tool를 만들어야 할 때 정말 유용하다. CMFCShellListCtrl과 함께 사용하면 간단한 윈도우 쉘을 만들 수 있다. 기본적인 쉘 기능들이 대부분 들어가 있으며, 사용법도 간단하다.

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void CMy2010ControlTestDlg::OnBnClickedButton2()
{
    // TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
    CString szSelectDir;
 
    m_cTestShellTreeCtrl.GetItemPath(szSelectDir, m_cTestShellTreeCtrl.GetSelectedItem());
 
    MessageBox(szSelectDir, L"경로");
}
 
void CMy2010ControlTestDlg::OnTvnSelchangedMfcshelltree1(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMTREEVIEW pNMTreeView = reinterpret_cast<lpnmtreeview>(pNMHDR);
    // TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
 
    CString szStr;
    szStr = m_cTestShellTreeCtrl.GetItemText(m_cTestShellTreeCtrl.GetSelectedItem());
    szStr += L"\n";
 
    OutputDebugString(szStr);
 
    *pResult = 0;
}
</lpnmtreeview>

 

CMFCShellTreeCtrl::EnableShellContextMenu

 Enables or disables the shortcut menu.

 

CMFCShellTreeCtrl::GetFlags

 Returns a combination of flags that are passed to IShellFolder::EnumObjects.

 

CMFCShellTreeCtrl::GetItemPath

 Retrieves the path to an item.

 

CMFCShellTreeCtrl::GetRelatedList

 Returns a pointer to the CMFCShellListCtrl Class object that is used together with this CMFCShellTreeCtrl object to create an Explorer-like window.

 

CMFCShellTreeCtrl::OnChildNotify

 This member function is called by this window's parent window when it receives a notification message that applies to this window.(Overrides CWnd::OnChildNotify.)

 

CMFCShellTreeCtrl::OnGetItemIcon

 

 

CMFCShellTreeCtrl::OnGetItemText

 

 

CMFCShellTreeCtrl::Refresh

 Refreshes and repaints the current CMFCShellTreeCtrl object.

 

CMFCShellTreeCtrl::SelectPath

 Selects the appropriate tree control item based on a supplied PIDL or string path.

 

CMFCShellTreeCtrl::SetFlags

 Sets flags to filter the tree context (similar to the flags used by IShellFolder::EnumObjects).

 

CMFCShellTreeCtrl::SetRelatedList

 Sets a relation between the current CMFCShellTreeCtrl object and a CMFCShellListCtrl object.

 

http://msdn.microsoft.com/ko-kr/library/bb982944.aspx

 

 

CMFCShellListCtrl

 

 기본적인 쉘 기능들을 모두 갖고 있는 컨트롤이다. 상위로 올라가는 기본 기능이 없기 때문에 윈도우 폴더창처럼 상위로 가는 버튼을 따로 만들어 주던가, 팝업 메뉴로 뒤로 가기 기능등을 만들어주면 꽤나 괜찮게 사용할 수 있다. 해당 기능은 MSDN에 맴버함수 설명으로 쉽게 찾아 사용 할 수 있다.

 

 

 

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
28
29
30
31
void CMy2010ControlTestDlg::OnTvnSelchangedMfcshelltree1(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMTREEVIEW pNMTreeView = reinterpret_cast<lpnmtreeview>(pNMHDR);
    // TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
 
    CString szStr;
    szStr = m_cTestShellTreeCtrl.GetItemText(m_cTestShellTreeCtrl.GetSelectedItem());
    szStr += L"\n";
 
    OutputDebugString(szStr);
 
    CString szPath;
    m_cTestShellTreeCtrl.GetItemPath(szPath, m_cTestShellTreeCtrl.GetSelectedItem());
 
    m_cTestShellListCtrl.DisplayFolder(szPath);
 
    *pResult = 0;
}
 
//상위 폴더로 이동하도록 해보자. CMFCShellTreeCtrl도 상위로 이동하게 만들자.
void CMy2010ControlTestDlg::OnBnClickedButton3()
{
    CString szPath;
 
    // TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
    m_cTestShellListCtrl.DisplayParentFolder();
    m_cTestShellListCtrl.GetCurrentFolder(szPath);
    m_cTestShellTreeCtrl.SelectPath(szPath);
 
}
</lpnmtreeview>

 

CMFCShellListCtrl::DisplayFolder

 Displays a list of items that are contained in a provided folder.

 

CMFCShellListCtrl::DisplayParentFolder

 Displays a list of items that are contained in the folder that is the parent of the currently displayed folder.

 

CMFCShellListCtrl::EnableShellContextMenu

 Enables or disables the shortcut menu.

 

CMFCShellListCtrl::GetCurrentFolder

 Retrieves the path of the current folder.

 

CMFCShellListCtrl::GetCurrentFolderName

 Retrieves the name of the current folder.

 

CMFCShellListCtrl::GetCurrentItemIdList

 Returns the PIDL of the current list control item.

 

CMFCShellListCtrl::GetCurrentShellFolder

 Returns a pointer to the current Shell folder.

 

CMFCShellListCtrl::GetItemPath

 Returns the textual path of an item.

 

CMFCShellListCtrl::GetItemTypes

 Returns Shell item types that are displayed by the list control.

 

CMFCShellListCtrl::IsDesktop

 Checks if the currently selected folder is the desktop folder.

 

CMFCShellListCtrl::OnCompareItems

 The framework calls this method when it compares two items.(Overrides CMFCListCtrl::OnCompareItems.)

 

CMFCShellListCtrl::OnFormatFileDate

 Called when the framework retrieves the file date displayed by the list control.

 

CMFCShellListCtrl::OnFormatFileSize

 Called when the framework converts the file size of a list control.

 

CMFCShellListCtrl::OnGetItemIcon

 Called when the framework retrieves the icon of a list control item.

 

CMFCShellListCtrl::OnGetItemText

 Called when the framework converts the text of a list control item.

 

CMFCShellListCtrl::OnSetColumns

 Called by the framework when it sets the names of the columns.

 

CMFCShellListCtrl::Refresh

 Refreshes and repaints the list control.

 

CMFCShellListCtrl::SetItemTypes

 Sets the type of items displayed by the list control.

 

http://msdn.microsoft.com/ko-kr/library/bb983966.aspx

 

 

CMFCPropertyGridCtrl

 

 VS2008의 기본바에 있던 Property를 다로 Ctrl로 이용할 수 있게되었다. 리본바에서 프로퍼티창만 따로 사용할 일이 많았는 데, 이제 기본 컨트롤로 지원하기 때문에 손쉽게 사용할 수 있다. 간단한 정보는 이제 프로퍼티 컨트롤을 이용해서 Tool로 정보를 표시할 수 있다. 얼마나 유용한 컨트롤인가.. 이번에 추가된 컨트롤 중에서 Tool개발에 가장 많이 사용하게 될 녀석이 될것이다.

 

 

 

기본적인 사용법은 리본바에서 동일하다.

 

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
28
// 프로퍼티 초기화
void CMy2010ControlTestDlg::InitProperty(void)
{
    CMFCPropertyGridProperty* pGroup1 = new CMFCPropertyGridProperty(_T("Character"));
    COleVariant var3DLook((short)VARIANT_FALSE, VT_BOOL);
 
    pGroup1->AddSubItem(new CMFCPropertyGridProperty(_T("Name"), var3DLook,
        _T("캐릭터의 이름이다")));
 
    CMFCPropertyGridProperty* pProp = new CMFCPropertyGridProperty(_T("Skill"),
        _T("Dialog Frame"), _T("어떤 스킬을 기본 스킬로 할 것인가?"));
    pProp->AddOption(_T("None"));
    pProp->AddOption(_T("Skill01"));
    pProp->AddOption(_T("Skill02"));
    pProp->AddOption(_T("Skill03"));
    pProp->AllowEdit(FALSE);
 
    pGroup1->AddSubItem(pProp);
    pGroup1->AddSubItem(new CMFCPropertyGridProperty(_T("Note"), (COleVariant) _T("글을 적습니다."), _T("아무거나 도움이 될만한걸 적어")));
 
    m_cTestPropertyGridCtrl.AddProperty(pGroup1);
 
    INT32 iWidth;
    iWidth = m_cTestPropertyGridCtrl.GetLeftColumnWidth();
 
    m_cTestPropertyGridCtrl.SetGroupNameFullWidth(FALSE, TRUE);
 
}

 

 

http://msdn.microsoft.com/ko-kr/library/bb983759.aspx

 

 

VS2010은 무겁다.

 

 일단 오늘은 여기까지.. 나머지 컨트롤도 천천히 사용해고 추후 Tool은 2010으로 제작해서 사용해보는 것도 괜찮을 것 같다. 물론 몇가지 문제점이 해결되면 말이다.

 

 일단 VS2010 컴파일러가 너무 무겁다. 무거운 정도가 아니라 회사 컴퓨터(i7)는 모르겠지만, 우리집 컴퓨터(Intel® Core™2 Duo CPUE8400, 2G Ram)는 허리가 휘어지려고 하는 것 같다. 일단 프로젝트 생성도 느리지만, 이미 생성된 프로젝트를 열려고 할 때도 상당한 로딩 시간을 필요로 한다. 여러가지로 무거운 것 같아서 프로젝트 용량을 보니.. 기겁할 노릇이다. ㅡㅡ;;

 

Control테스트를 위해서 만든 샘플을 컴파일후 용량을 확인해보니.. 다음과 같았다.

 

               [간단한 샘플 프로그램 컴파일 후 하드 사용량]

 

 그림을 보면 알겠지만, 샘플 프로그램의 총 용량이 무려 364M나 한다. ㅡㅡ;; VS2008이 60M정도 하는 걸 보면 대략 6배 정도 늘어난 것을 확인할 수 있다. 물론 많은 정보를 모아서 보다 편리한 IDE를 지원해주기 위한 용도겠지만.. 그렇다고 해도.. 너무 큰 용량이 아닌가 싶다. 아무리 시대가 테라 단위로 가는 하드라고 해도.. 이건 좀 아닌 것 같다.

 

 그래 좋다. 용량크고, 그리고 솔루션 로딩이 느릴수 있다. 하지만 문제는 컴파일 속도도 무지하게 느리다는 것이다. 아마도 최신 컴퓨터를 기준으로 만들어진 2010이겠지만 이정도 하드웨어 사항에서도 버벅거리는 건.. 상당히 무거운 컴파일러가 아닌가 싶다. 무척이나 큰 프로젝트고 컴파일만 몇십분 하기라도 하면.. 어떨지 무지 궁금하다. 오히려 더 빠를 수도 있겠지만.. (처음 초기화만 느리고 컴파일 자체가 빠르다면..) 회사에서 라이선스를 구입하면 한번 기존 프로젝트를 변환해서 정밀하게 테스트 해볼 필요가 있을 듯 싶다. 물론 인크리드 빌드 같은 프로그램을 이용하면 빠르겠지만… 결론적으로 매우 무겁다.

 

  

 2010ControlTest.exe 

 2010ControlTest.zip

 

반응형
반응형
http://seolis.tistory.com/105


MFC PropertyGrid Control (Column Header Size 조절하기)

programming/MFC 2011/11/21 11:05
Visual Studio 2010 부터는 "MFC PropertyGird Control"을 지원해줍니다. 2008 이하 버전부터는 따와(?)야 되죠 -ㅇ-;;


암튼 오랜만에 2010을 쓰기도하고, 처음으로 2010버전 PropertyGird Control을 사용했는데..

처음부터 난관(?)에 부딪혔습니다. 밑에 그림처럼 1번째 Column의 사이즈가 작게 나오더군요 -.-;;



해결 방법은 간단합니다.

컨트롤 변수를 선언해주시고. 밑에 소스 추가합니다.

 HDITEM item;
 item.cxy = 200;  // Size 
 item.mask = HDI_WIDTH;
 m_ctrlGridControl.GetHeaderCtrl().SetItem(0, &HDITEM(item));  //  m_ctrlGridControl => Control 변수 // Header Column Size 설정


(2008 이하 버전들도 동일할 것 입니다)


짜잔!

아마..아마도...저처럼 검색하는 분들이 있을거라..믿고..적어봅니다 =ㅇ=ㅋㅋ

반응형
반응형

http://msdn.microsoft.com/ko-kr/library/windows/desktop/ms682483(v=vs.85).aspx








Creates a queue for timers. Timer-queue timers are lightweight objects that enable you to specify a callback function to be called at a specified time.



Syntax

HANDLE WINAPI CreateTimerQueue(void);

Parameters

This function has no parameters.

Return value

If the function succeeds, the return value is a handle to the timer queue. This handle can be used only in functions that require a handle to a timer queue.

If the function fails, the return value is NULL. To get extended error information, callGetLastError.






Remarks

To add a timer to the queue, call the CreateTimerQueueTimer function. To remove a timer from the queue, call the DeleteTimerQueueTimer function.

When you are finished with the queue of timers, call the DeleteTimerQueueEx function to delete the timer queue. Any pending timers in the queue are canceled and deleted.

To compile an application that uses this function, define _WIN32_WINNT as 0x0500 or later. For more information, see Using the Windows Headers.



BLOG main image

0x0500 은 윈도우 2000 을 말한다, XP 보다도 낮은버전

Windows XP =_WIN32_WINNT>=0x0501

WINVER>=0x0501

Windows 2000 =_WIN32_WINNT>=0x0500

WINVER>=0x0500



 









An application-defined function that serves as the starting address for a timer callback or a registered wait callback. Specify this address when calling theCreateTimerQueueTimerRegisterWaitForSingleObject function.

The WAITORTIMERCALLBACK type defines a pointer to this callback function.WaitOrTimerCallback is a placeholder for the application-defined function name.

Syntax

VOID CALLBACK WaitOrTimerCallback(
  _In_  PVOID lpParameter,
  _In_  BOOLEAN TimerOrWaitFired
);

Parameters

lpParameter [in]

The thread data passed to the function using a parameter of theCreateTimerQueueTimer or RegisterWaitForSingleObject function.

TimerOrWaitFired [in]

If this parameter is TRUE, the wait timed out. If this parameter is FALSE, the wait event has been signaled. (This parameter is always TRUE for timer callbacks.)

Return value

This function does not return a value.

Remarks

This callback function must not call the TerminateThread function.

For an example that uses this callback function, see Using Timer Queues.

Requirements

Minimum supported client

Windows XP [desktop apps only]

Minimum supported server

Windows Server 2003 [desktop apps only]

Header

WinBase.h on Windows XP, Windows Server 2003, Windows Vista, Windows 7, Windows Server 2008, and Windows Server 2008 R2 (include Windows.h);
Winnt.h on Windows 8 and Windows Server 2012



 







예제


#include <windows.h>
#include <stdio.h>

HANDLE gDoneEvent;

VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired)
{
    if (lpParam == NULL)
    {
        printf("TimerRoutine lpParam is NULL\n");
    }
    else
    {
        // lpParam points to the argument; in this case it is an int

        printf("Timer routine called. Parameter is %d.\n", 
                *(int*)lpParam);
        if(TimerOrWaitFired)
        {
            printf("The wait timed out.\n");
        }
        else
        {
            printf("The wait event was signaled.\n");
        }
    }

    SetEvent(gDoneEvent);
}

int main()
{
    HANDLE hTimer = NULL;
    HANDLE hTimerQueue = NULL;
    int arg = 123;

    // Use an event object to track the TimerRoutine execution
    gDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (NULL == gDoneEvent)
    {
        printf("CreateEvent failed (%d)\n", GetLastError());
        return 1;
    }

    // Create the timer queue.
    hTimerQueue = CreateTimerQueue();
    if (NULL == hTimerQueue)
    {
        printf("CreateTimerQueue failed (%d)\n", GetLastError());
        return 2;
    }

    // Set a timer to call the timer routine in 10 seconds.
    if (!CreateTimerQueueTimer( &hTimer, hTimerQueue, 
            (WAITORTIMERCALLBACK)TimerRoutine, &arg , 10000, 0, 0))
    {
        printf("CreateTimerQueueTimer failed (%d)\n", GetLastError());
        return 3;
    }

    // TODO: Do other useful work here 

    printf("Call timer routine in 10 seconds...\n");

    // Wait for the timer-queue thread to complete using an event 
    // object. The thread will signal the event at that time.

    if (WaitForSingleObject(gDoneEvent, INFINITE) != WAIT_OBJECT_0)
        printf("WaitForSingleObject failed (%d)\n", GetLastError());

    CloseHandle(gDoneEvent);

    // Delete all timers in the timer queue.
    if (!DeleteTimerQueue(hTimerQueue))
        printf("DeleteTimerQueue failed (%d)\n", GetLastError());

    return 0;
}




 


반응형
반응형

BLOG main image






 닫기버튼(x닫기)이나 alt+F4 종료하게되면


WM_CLOSE 메세지가 프레임 윈도우에 발생한다


mainframe::OnClose() 함수가 먼저 호출됨










http://blog.naver.com/hoi_yeon/10020732860


WM_CLOSE

윈도우가 닫히기 전에 이 메세지가 전달되며 메인 윈도우인 경우는 응용 프로그램이 종료된다는 신호.

이 메세지를 처리하지 않고 DefWindowProc으로 보내면 DestroyWindow 함수를 호출하여 윈도우를 파괴하도록한다.

이 메제지가 전달되었을 때는 아직 윈도우가 파괴된 것이 아니므로 윈도우가 파괴되는 것을 중간에 차단할 수 있다. 미저장 파일이 있거나 프로그램을 종료할 상황이 되지 않을때 사용자에게 메세지 박스를 통해 종료 사실을 확인 시킬수 있으며 이 메세지를 가로채서 단순히  return하면 DestroyWindow가 호출되지 않도록 할 수 있다.

WM_DESTROY

윈도우가 파괴될 때 이 메세지가 전달된다. 사용자가 Alt+F4 또는 닫기버튼을 누를 경우 WM_CLOSE 메세지가 전달되며 이 메세지를 별도로 처리하지 않으면 DefWindowProcDestroyWindow함수를 호출하여 윈도우를 파괴한다. 또는 프로그램 코드 내부에서 명시적으로  DestroyWindow함수를 호출할 때도 윈도우가 파괴되는데 이 함수 호출결과로 WM_DESTROY 메세지가 전달된다.

이 메세지를 받은 윈도우의 종료를 위한 처리를 해야 하는데 예를 들어 열어놓은 파일을 닫고 할당한 메모리를 해제하는 등의 정리 작업을 한다. WM_CREATE에서의 초기화 처리의 반대 동작이 이 메세지에 작성되는것이 일반적이며 그 외 레지스트리에 미보관 정보를 저장하는 등의 작업을 할 수 있다. 만약 파괴되는 윈도우가 클립보드 체인에 속해 있으면 자신을 클립보드 체인에서 제거해야한다.

DestroyWindow 함수는 파괴할 윈도우를 화면에서 숨긴 후 이 메세지를 보내므로 이 메세지를 받은 시접에서는 윈도우 자체가 파괴되지 않은 상태이다. 또한 DestroyWindow 함수는 자식 윈도우에게도 이 메세지를 차례대로 보내주는데 부모 윈도우가 먼저 이 메세지를 받고 자식 윈도우에게로 이 메세지가 보내진다. 따라서 부모 우니도우가 이 메세지를 처리하는 동안은 모든 자식 윈도우가 아직 파괴되기 전이므로 자식 윈도우를 프로그래밍할 수 있다.

파괴되는 윈도우가 메인 윈도우일 경우 PostQuitMessage 함수르 ㄹ반드시 호출하여 프로세스의 메시지 루프를 종료하도록 해야 한다. 만약 이 처리를 생략하면 윈도우만 파괴되고 메세지 루프는 계속 실행중인 상태가 되므로 프로세스가 종료되지 않는다.

 

출처 - www.winapi.co.kr

[출처] WM_CLOSE / WM_DESTROY|작성자 바부

반응형
반응형

BLOG main image




BOOL CtestApp::InitInstance()

{

m_pMainWnd->ShowWindow(SW_SHOWMAXIMIZED); //창 최대화에 대한 함수를 추가해주면 된다

m_pMainWnd->UpdateWindow();


}

반응형
반응형


http://iq_up.blog.me/100125952192 

 

 

 

 

 

VC++ 에서

AfxMessageBox 및
MessageBox 사용 예

C:\Program Files\Microsoft Visual Studio\VC98>findstr /c:"MB_ICON" /s *.h
Include\MSI.H://  A message box icon type:  MB_ICON*, where no icon is the default
Include\MSIQUERY.H:// MB_ICONERROR, MB_ICONQUESTION, MB_ICONWARNING, MB_ICONINFORMATION
Include\WINUSER.H:#define MB_ICONHAND                 0x00000010L
Include\WINUSER.H:#define MB_ICONQUESTION             0x00000020L
Include\WINUSER.H:#define MB_ICONEXCLAMATION          0x00000030L
Include\WINUSER.H:#define MB_ICONASTERISK             0x00000040L
Include\WINUSER.H:#define MB_ICONWARNING              MB_ICONEXCLAMATION
Include\WINUSER.H:#define MB_ICONERROR                MB_ICONHAND
Include\WINUSER.H:#define MB_ICONINFORMATION          MB_ICONASTERISK
Include\WINUSER.H:#define MB_ICONSTOP                 MB_ICONHAND
Include\WINUSER.H:#define MB_ICONMASK                 0x000000F0L


Include\MSIQUERY.H://MB_OK,MB_OKCANCEL,MB_ABORTRETRYIGNORE,MB_YESNOCANCEL,MB_YESNO,MB_RETRYCANCEL
Include\MSIQUERY.H:// Returns Win32 button codes: IDOK IDCANCEL IDABORT IDRETRY IDIGNORE IDYES IDNO


MessageBox(hDlg,"message","title",MB_ICONWARNING|MB_YESNO);
이렇게 할 수 도 있고.

MessageBox(0,"desktop message","title",MB_ICONWARNING|MB_YESNO);
MessageBox(hDlg,"message","title",MB_ICONWARNING|MB_OK);

::AfxMessageBox("AfxMessageBox(\"메시지\")");
::AfxMessageBox("MB_ICONHAND",MB_ICONHAND);
::AfxMessageBox("MB_ICONQUESTION",MB_ICONQUESTION);
::AfxMessageBox("MB_ICONEXCLAMATION",MB_ICONEXCLAMATION);
::AfxMessageBox("MB_ICONASTERISK",MB_ICONASTERISK);
::AfxMessageBox("MB_ICONWARNING",MB_ICONWARNING);
::AfxMessageBox("MB_ICONERROR",MB_ICONERROR);
::AfxMessageBox("MB_ICONINFORMATION",MB_ICONINFORMATION);
::AfxMessageBox("MB_ICONSTOP",MB_ICONSTOP);
::AfxMessageBox("MB_ICONMASK",MB_ICONMASK);
::AfxMessageBox("MB_ICONMASK | MB_ABORTRETRYIGNORE",MB_ICONMASK | MB_ABORTRETRYIGNORE);//
::AfxMessageBox("MB_DEFBUTTON3 는 3번째 버튼을 디폴트로 활성화 합니다.(1~3)",MB_ICONMASK | MB_ABORTRETRYIGNORE|MB_DEFBUTTON3);//


::AfxMessageBox("AfxMessageBox(\"메시지\")");

유의: 아이콘에 대한 정의가 없으면 기본적으로 경고느낌표이다.

아래의 알림느낌표와 경고느낌표의 아이콘이 다름에도 유의하자.

- 정의하지 않거나,MB_ICONWARNING,MB_ICONEXCLAMINATION 이거나 3가지 경우이다.

 

::AfxMessageBox("MB_ICONHAND",MB_ICONHAND);


::AfxMessageBox("MB_ICONQUESTION",MB_ICONQUESTION);


::AfxMessageBox("MB_ICONEXCLAMATION",MB_ICONEXCLAMATION);



::AfxMessageBox("MB_ICONASTERISK",MB_ICONASTERISK);
알림 느낌표이다.

위의 경고느낌표와는 느낌이 조금 다르다.

Asterisk 는 별표이다. information으로 해도 된다.


::AfxMessageBox("MB_ICONWARNING",MB_ICONWARNING);

                                                         



::AfxMessageBox("MB_ICONERROR",MB_ICONERROR);



::AfxMessageBox("MB_ICONINFORMATION",MB_ICONINFORMATION);




::AfxMessageBox("MB_ICONSTOP",MB_ICONSTOP);

 



::AfxMessageBox("MB_ICONMASK",MB_ICONMASK);




::AfxMessageBox("MB_ICONMASK | MB_ABORTRETRYIGNORE",MB_ICONMASK | MB_ABORTRETRYIGNORE);


::AfxMessageBox("MB_DEFBUTTON3 는 3번째 버튼을 디폴트로 활성화 합니다.(1~3)",MB_ICONMASK | MB_ABORTRETRYIGNORE|MB_DEFBUTTON3);

 

AfxMessageBox 자동으로 닫기

출처: http://k.daum.net/qna/view.html?category_id=QCA&qid=0CoCn&q=AfxMessageBox

 

저는 OnTimer를 이용해서 닫는 방법을 소개하지요.. 
Test란 이름으로 Project를 만들었을 경우 

void CTestDlg::OnTest() 

SetTimer(1, 500, NULL); 
AfxMessageBox("kdkdkd"); 


void CTestDlg::OnTimer(UINT nIDEvent) 

CString strCaption; // 윈도우 캡션 
CWnd* pWnd=AfxGetMainWnd()->GetWindow(GW_HWNDFIRST); 
while(pWnd) { 
if(pWnd->IsWindowVisible() && 
pWnd->GetWindowTextLength() && pWnd->GetOwner != NULL) { 
pWnd->GetWindowText(strCaption); 
if(strstr(strCaption,"TEST") != NULL) break; 

pWnd = pWnd->GetWindow(GW_HWNDNEXT); 


if(pWnd) 

pWnd->SendMessage(WM_CLOSE, 0, 0L); 
KillTimer(1); 

CDialog::OnTimer(nIDEvent); 
}

================================================

참고: http://msdn.microsoft.com/ko-kr/library/w0kywcw3.aspx

http://msdn.microsoft.com/ko-kr/library/as6se7cb.aspx

반응형
반응형

블로그 이미지

3DMP engines

3D그래픽스 물리 수학, 프로그래밍 GPU Shader 게임엔진 알고리즘 디자인패턴 matlab etc..

by 송정헌




// 명령줄에 지정된 명령을 디스패치합니다.

// 응용 프로그램이 /RegServer, /Register, /Unregserver 또는 /Unregister로 시작된 경우 FALSE를 반환합니다.

if (!ProcessShellCommand(cmdInfo))

return FALSE;


ProcessShellCommand 가 호출되고 난 후 view 클래스의 OnInitialUpdate 함수가 호출됨을 알 수있다


이때 OnInitialUpdate 함수가 끝나면 다시 ProcessShellCommand 의 아래부터 실행된다

반응형
반응형

http://www.winapi.co.kr/

 

 

1.소개

스파이는 비주얼 C++과 함께 배포되는 개발 보조툴로 주로 윈도우에 대한 정보를 조사하고자 할 때 사용된다. 윈도우의 클래스 이름이나 스타일 또는 포함 관계를 조사하거나 윈도우로 전달되는 메시지를 감시하는 것이 주된 용도이며 이 툴을 사용하면 다른 프로그램의 구성이나 동작 방식에 대해 많은 것을 배울 수 있다. 이름 그대로 이미 만들어져 있는 윈도우의 정보를  몰래 살짝 들여다 보는 도구이다. 그다지 실용적이지는 않지만 스레드나 프로세스에 대한 감시 기능도 제공한다.

스파이가 제공하는 정보는 Win32 API의 표기법을 그대로 사용하므로 이 정보를 제대로 해석하려면 Win32 API에 대한 기본적인 이해가 필요하다. 예를 들어 윈도우 스타일은 WS_CHILD나 WS_VSCROLL 등으로 표기되는데 이 상수들이 어떤 의미를 가지는가를 알고 있어야 하며 윈도우 클래스의 각 멤버에 대한 이해도 필요하다. 클래스 바이트(cbClsExtra)나 인스턴스 핸들, 윈도우 프로시저같은 용어에도 거부감이 없어야 한다. 이 강좌를 읽고 있는 사람이라면 이런 기본적인 용어에 대해서는 잘 알고 있을 것이다.

비주얼 스튜디오의 모든 버전에 포함되어 있기 때문에 별도로 설치할 필요가 없으며 각 버전별로 기능상의 차이점도 거의 없으므로 한가지 버전에 대해서만 사용법을 익혀두면 된다. 이 강좌는 비주얼 스튜디오 .Net의 Spy++로 작성되었다. 비주얼 C++ 6.0에도 스파이가 있고 더 최신의 닷넷 2003에도 포함되어 있는데 사용 방법은 비슷하다.

시작 버튼을 눌러 프로그램 메뉴를 열고 비주얼 스튜디오 폴더의 Visual Studio .Net Tools를 선택하면 이 안에 Spy++이 등록되어 있으며 이 항목을 선택하면 스파이가 실행된다. 한번만 실행되는 프로그램이므로 이미 실행중이라면 실행중인 스파이로 포커스만 넘어간다. 자주 사용한다면 단축 아이콘을 만들어 놓는 것이 좋을 것이다. 스파이는 다음과 같이 생겼다.

MDI 형태로 되어 있으므로 여러 개의 뷰를 동시에 열어 놓을 수 있으나 실제 중요한 정보는 개별적인 대화상자를 통해 확인하기 때문에 MDI의 이점은 별로 없는 셈이다. 대체로 관심있는 뷰 하나만 열고 최대화해 놓고 많이 쓰는 편이다.

비주얼 C++ 6.0까지는 영문으로 되어 있었으나 7.0부터는 스파이도 한글화되어 메뉴나 대화상자의 캡션이 한글로 바뀌었다. 그러나 개발자들은 실제로 영문으로 된 전문 용어에 익숙해져 있기 때문에 번역된 캡션이 더 어색하게 느껴지기도 한다. 번역이 그다지 매끄럽지는 않지만 번역된 캡션을 보면 어떤 대상을 가리키는지 직관적으로 알 수 있는 정도이다. 예를 들어 "창 바이트"라는 말은 윈도우의 여분 바이트(cbWndExtra)라는 뜻이고 "창 프로시저"라는 말은 윈도우 프로시저의 번지라는 뜻이다. 개인적으로 이런 전문적인 프로그램은 한글화 하지 않는 것이 더 좋다고 생각한다.

타이틀 바 아래에 메뉴가 있고 그 아래에 툴바가 있으며 작업영역 아래에는 상태란이 배치되어 있어 전체적인 모양은 지극히 평범하다. 메뉴에는 스파이의 모든 명령들이 정리되어 있겠지만 대부분의 명령들이 툴바에 있으므로 이 버튼들에 대해서만 알아 두면 스파이의 모든 기능을 다 활용할 수 있다.

메뉴에 있는 명령 중 유일하게 실용성이 있는 명령은 메인 윈도우의 글꼴을 변경할 수 있는 보기/글꼴 명령밖에 없다. 디폴트 글꼴은 9포인트의 Sans Serif인데 굴림이나 바탕으로 변경할 수 있으며 글꼴의 크기도 크게 만들 수 있다. 다음은 툴 버튼에 대한 간략한 설명이다.

 

버튼

설명

 뷰를 보여준다.

프로세스 뷰를 보여준다.

스레드 뷰를 보여준다.

메시지 옵션 대화상자를 보여준다.

 찾기 대화상자를 띄운다.

메시지 기록을 시작하거나 잠시 중지한다.

메시지 옵션 대화상자를 보여준다.

지금까지 조사된 메시지 기록을 삭제한다.

조건에 맞는 프로세스스레드메시지를 검색한다.

다음 찾기

이전 찾기

 속성 대화상자를 보여준다.

새로 고침.

 

상태란은 선택된 메뉴나 툴바에 대한 간단한 도움말을 보여주는 정도의 기능밖에 없다. 겉으로 보이는 이런 것들 외에 화면 각 부분을 누르면 나타나는 팝업 메뉴가 있는데 이에 대해서는 관련 부분에서 따로 설명하기로 한다.

 


 

6.메시지 뷰

메시지 뷰는 윈도우로 전달되는 모든 메시지를 감시하여 어떤 메시지가 어떤 인수와 함께 전달되었는지를 보여준다. 프로세스나 스레드로 전달되는 메시지를 감시할 수도 있지만 주로 윈도우의 메시지를 감시하는 경우가 많다. 전달되는 모든 메시지를 순서대로 살펴볼 수 있기 때문에 디버깅과 분석시에 아주 큰 도움이 된다.

메시지 뷰를 열 때는 각 뷰의 팝업 메뉴에서 메시지 항목을 선택하면 된다. 예를 들어 메모장의 에디트 컨트롤로 전달되는 메시지를 보고 싶다면 창 뷰에서 에디트 컨트롤을 선택하고 팝업 메뉴에서 메시지 항목을 선택하면 된다. 또는 창 찾기 대화상자에서 찾기 도구를 메모장의 에디트에 떨어뜨린 후 대화상자 아래의 표시 항목을 메시지로 선택해도 된다.

 

메시지 기록이 시작되면 메시지 뷰가 열리며 여기에 해당 윈도우로 전달되는 메시지들이 순서대로 출력된다. 스파이는 메시지 감시를 위해 후킹을 사용하기 때문에 메시지를 감시하는동안에는 시스템의 전반적인 속도가 눈에 띄게 느려지는데 속도만 느려질 뿐 기능상의 문제는 없다. 윈도우로 전달되는 메시지를 일일이 감시하고 문자열로 바꾸어 출력하고 있으니 느려질 수밖에 없다. 다음은 메모장으로 전달되는 메시지들이다.

제일 왼쪽의 번호는 메시지의 일련 번호이며 두번째 칸의 16진수는 메시지를 받은 윈도우의 핸들이다. 세번째 P,R,S,s는 메시지가 전달된 방식을 나타내는 메시지 코드가 출력된다. 메시지 코드는 다음과 같다.

 

코드

설명

P

PostMessage 함수로 메시지 큐에 붙여진 메시지이다.

S

SendMessage 함수에 의해 윈도우 프로시저로 직접 전달된 메시지이다.

R

S 보내진 메시지를 처리한 결과이다메시지 처리 중에 다른 메시지를  보낼  있기 때문에 S R 중첩될  있다.

s

S 유사하되 보안상의 이유로  메시지의 처리 결과를 조사할  없다.

 

메시지 코드 다음에는 실제 전달된 메시지와 메시지의 인수들이 표시되는데 각 메시지별로 의미가 해석되어 있다. 예를 들어 WM_KEYDOWN이면 어떤 키가 눌러졌고 반복 회수는 얼마이며 스캔코드와 확장키의 상태 등이 표시되며 WM_CHAR이면 입력된 문자를 보여준다. wParam, lParam을 보여주는 것이 아니라 인수로 전달된 메시지의 논리적인 실제값을 보여주기 때문에 메시지의 의미를 파악하기가 쉽도록 되어 있다.

윈도우로 아주 많은 메시지가 전달될 뿐만 아니라 전달되는 속도가 대단히 빠르기 때문에 순식간에 수백개의 메시지가 전달된다. 그러다 보니 정작 관심있는 메시지가 너무 빨리 위로 스크롤되어 버리는 불편함이 있는데 스파이는 원하는 메시지를 자세히 살펴 볼 수 있도록 몇가지 장치를 제공한다.

우선 툴바의  버튼을 누르면 기록을 잠시 중지/재개할 수 있다. 살펴보고 싶은 메시지가 전달되었으면 메시지 기록을 일단 중지하고 목록을 살펴볼 수 있다.  버튼은 지금까지 작성한 모든 기록을 삭제하고 다시 기록을 시작하도록 한다.  버튼은 메시지 기록에 대한 몇가지 옵션을 지정하는 다음 대화상자를 보여 준다.

세 개의 페이지로 구성되어 있는데 이 대화상자에도 찾기 도구가 있으므로 대상 윈도우를 직접 변경할 수 있다. 뿐만 아니라 추가 창을 선택하면 관련있는 주변 윈도우로 전달되는 메시지도 같이 감시할 수 있다. 메시지 페이지에서는 기록 대상 메시지를 선택한다.

디폴트로 모든 메시지를 다 기록하도록 되어 있는데 여기서 관심있는 메시지만 선택하거나 불필요한 메시지는 선택 취소할 수 있다. 예를 들어 다른 메시지는 관심이 없고 키보드 관련 메시지만 보고 싶다면 Keyboard만 선택하면 된다. 또한 WM_MOUSEMOVE, WM_NCHITTEST 같은 메시지는 너무 자주 발생하므로 제외시킬 수도 있다. 출력 페이지는 메시지의 출력 형식을 지정한다.

파일에도 로그 옵션을 선택하고 파일명을 주면 메시지 기록을 텍스트 파일에도 작성해 주므로 조사가 끝난 후 파일을 열어서 볼 수 있다. 메시지 전달 속도가 너무 빠르기 때문에 화면으로 확인하기 힘든 메시지는 파일에서 정밀 검색하면 된다.

메시지 뷰가 보여 주는 정보는 아주 다양하게 활용된다. 우선 디버깅에 활용되는데 메시지가 전달되는 순서와 처리되는 과정을 자세하게 살펴 볼 수 있어 프로그램의 동작을 세밀하게 감시할 수 있다. 프로그램이 이상 동작을 한다면 그 시점이 언제인지를 스파이로 조사할 수 있으며 일단 시점이 파악되면 디버거로 해당 메시지를 집중 디버깅해 보면 된다. 또한 원치않은 메시지가 발생한다면 어떤 메시지에 의해 추가로 발생한 메시지인지, 그 순서는 어떻게 되는지를 알 수 있으며 SendMessage로 보낸 메시지를 확실히 수신했는지 확인할 수도 있다.

Win32 API를 처음 공부하는 사람에게 스파이는 또한 학습용으로도 아주 가치가 있는데 예를 들어 IME같이 메시지의 순서나 파생 관계가 복잡할 때 스파이를 활용하면 메시지의 발생 시기와 순서을 정확하게 알 수 있다.

 

반응형
반응형

http://openarisu.tistory.com/191


다음 순서로 진행이 된다. 
알아두자.

1. WM_CLOSE (OnClose) 발생
2. DestoryWindow (DestoryWindow )  호출 
3. WM_DESTORY ( OnDestory) 발생

각 함수를 수정할려면
WM_CLOSE 메시지 선택시 OnClose()
DestoryWindow 재정의할 때 DestoryWindow()
WM_DESTROY 메시지 선택시 OnDestory() 함수를 작성할 수 있다.

소스 내용 보기.
void CTestDlg::OnClose()
{
    // TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
    >> 이 부분에서 종료되는 윈도우를 제어할 수 있습니다.
    CDialog::OnClose();
}

BOOL CTestDlg::DestroyWindow()
{
    return CDialog::DestoryWindow();
}

void CTestDlg::OnDestory()
{
    CDialog::OnDestory();
}


참조 : http://yunhyeon.tistory.com/tag/DestroyWindow

반응형
반응형

http://blog.naver.com/mirzzz79/110013137412


MFC Single Document FrameWork

  - MFC 주요 클래스의 호출 순서는 다음과 같이 이루어지며,

     클래스 공유되어 사용되는 리소스 클래스를 포인터로 연결하여 사용할때

     소멸 순서에 의하 잘못된 포인터 참조를 막기위해, 리소스는 Document에

     생성하여 링크하는 것이 가장 효율적이며, 관리하기 좋다. 

 

## Construct CApp ##
CApp::CApp()      

CApp::InitInstance()

 

## Construct CDoc ##
CDoc::CDoc()      

 

## Construct CMainFrame ##
CMainFrame::CMainFrame()   
CMainFrame::PreCreateWindow(CREATESTRUCT& cs) 
CMainFrame::Create(LPCTSTR lpszClassName, ...)
CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
CMainFrame::OnWndMsg(UINT message, ...)
CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

 

## Construct CView ##
CView::CView()      
CView::Create(LPCTSTR lpszClassName, ...)
CView::PreCreateWindow(CREATESTRUCT& cs)
CView::OnCreate(LPCREATESTRUCT lpCreateStruct)

 

## Document Restore ##
CDoc::OnNewDocument()  -> 내부 Pre Call - CDoc::DeleteContents()

## View Setting ##
CView::OnInitialUpdate()

CMainFrame::OnWndMsg(UINT message, ...)
CMainFrame::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
CView::OnDraw(CDC* /*pDC*/)

 

## Thread Setting ##
CApp::Run()

 

## Loop ##
whlie(true)
{
 CApp::OnIdle(LONG lCount)
 CApp::PreTranslateMessage(MSG* pMsg)
 CMainFrame::OnWndMsg(UINT message, ...)
 CMainFrame::PreTranslateMessage(MSG* pMsg)
 CMainFrame::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
 CView::OnDraw(CDC* /*pDC*/)
}

 

CMainFrame::OnClose()

 

## Document Release ##
CDoc::OnCloseDocument()  -> 내부 Post Call - CDoc::DeleteContents()

CMainFrame::DestroyWindow()
CMainFrame::OnDestroy()

 

## Destroy CView ##
CView::OnDestroy()
CView::~CView()      


## Destroy CMainFrame ##
CMainFrame::~CMainFrame()   

 

## Destroy CDoc ##
CDoc::~CDoc()

CApp::ExitInstance()

 

## Destroy CApp ##

 

 

by. mirz (2007.1)

반응형
반응형

http://mins79.tistory.com/entry/%EA%B8%B0%EB%B3%B8%EC%A0%81%EC%9D%B8-%EC%82%AC%EC%9A%A9%EB%B2%95-MFC-ComboBox-%EC%98%88%EC%A0%9C

 

콤보박스를 어떻게 다루어야 하는지에 대하여 알아보겠습니다.

보통 사용한다함은?
1. 데이터를 추가한다.
2. 데이터를 삽입한다.
3. 데이터 하나를 지운다.
4. 데이터를 모두 지운다.
5. 부가정보를 첨부한다.
6. 현재 선택되어진 문자열을 읽어온다.
이정도 선에서 사용하게 됩니다.

우선 콤보박스를 빈 다이알로그에 하나 올리고 아이디를 IDC_COMBO1 이라고 하고 다음과 
같은 스타일로 샘플을 만들겠습니다.


다음은 CComboBox m_combo 로 컨트롤을 연결합니다.




다음으로 위에서 열거한 기능을 하나씩 버튼을 넣어가면 진행하여 보겠습니다.


1. 우선 그림처럼 버튼을 넣고, 그 버튼을 누르면 데이터를 추가합니다.

void CSssDlg::OnButton1() 
{
    m_combo.AddString("하나");
    m_combo.AddString("둘");
    m_combo.AddString("셋");
}


int CComboBox::AddString(LPCTSTR lpszString);
-------------------------------------------------
Parameters
   lpszString - NULL로 끝나는 문자열
Return Value
   추가된 아이템의 크기가 0보다 크다면 Zero-Based 인덱스가 리턴된다.
   만약 CB_ERRSPACE가 리턴된다면 데이터를 저장하기 위해 할당할 메모리가 모자라다.
Remark
   만약 생성시에 CBS_SORT 스타일을 넣었다면 정렬된 위치로 삽입되게 된다. 


2. 다음 그림처럼 버튼을 넣고, 그 버튼을 누르면 데이터를 2번째에 삽입한다.

void CSssDlg::OnButton2() 
{
   m_combo.InsertString(1, "삽입됨");
}

인덱스가 Zero-Based Index 이기 때문에 인덱스 1에다 넣으면 2번째 위치가 된다.

int CComboBox::InsertString(int nIndex, LPCTSTR lpszString);
--------------------------------------------------------------
Parameter
   nIndex - Zero-Based Index 이고, 만약 -1이면 마지막에 추가된다. 
   lpszString - NULL로 끝나는 문자열
Return Value
   현재 삽입된 위치의 인덱스가 리턴된다.
Remark
   AddString과는 달리 CBS_SORT 스타일이라 하더라도, 삽입위치는 변경되지 않는다.


3. 다음 그림처럼 버튼을 넣고, 그 버튼을 누르면 선택되어진 아이템을 삭제한다.

void CSssDlg::OnButton3() 
{
    int nIndex = m_combo.GetCurSel();
    if(nIndex == -1)
        return;
    m_combo.DeleteString(nIndex);
}

아무것도 선택되지 않았거나, 아이템이 하나도 없을 경우에 대한 에러처리를 해야한다.

int CComboBox::DeleteString(UINT nIndex);
----------------------------------------
Parameter
   nIndex - 0보다 크거나 같은 삭제할 인덱스
Return Value
   0이거나 0보다 큰값으로 현재 남아있는 아이템의 카운트를 리턴한다.
Remark
   당연한 이야기겠지만, 여러개의 아이템중에 중간에 것을 삭제하면 아래의 하위 아이템들의
   인덱스가 당연히 하나씩 줄어든다. 아이템을 인덱스로 관리할 경우 에러가 나거나 오동작
   할 수 있으므로 주의해야한다.


4. 다음 그림처럼 버튼을 넣고, 그 버튼을 누르면 데이터를 모두 지운다.

void CSssDlg::OnButton4() 
{
   m_combo.ResetContent();
}

루프를 돌며 하나씩 지워나갈 수도 있겠지만, 지원하는 함수가 있으니...

void CComboBox::ResetContent();
-----------------------------------
Remark
   루프를 돌면서 하나식 지우게 되면 에디트의 선택영역이 글자는 남아있게된다. 이 함수를 
   이용하게 되면 에디트와 리스트영역을 모두 초기화 시킨다.


5. 다음 그림처럼 버튼을 넣고, 그 버튼을 누르면 개별 아이템에 부가정보를 첨부한다.
첨부된 부가 정보를 보여주기 위하여 콤보박스 하단에 스태틱 컨트롤을 하나추가한다.
또한 아이템의 선택이 변경되었을 때마다 스태틱의 정보를 갱신해 주기 위하여 CBN_SELCHANGE
이벤트를 추가하고 핸들러에 부가 코드를 넣는다.

// 아래의 이벤트는 콤보박스에서 아이템을 선택을 변경하면 발생하는 이벤트이다.
void CSssDlg::OnSelchangeCombo1() 
{
    // 현재 선택되어진 아이템
    int nIndex = m_combo.GetCurSel();
    if(nIndex == -1)
        return;

    CString s;
    // 현재 아이템의 부가 정보를 읽어온다.
    // 여기서는 단순하게 숫자로 넣었지만, 포인터를 넣을 수 있으므로 모든 데이터를 다
    // 첨부할 수 있다.
    // 디비에서 사용자 정보 전체를 읽어와서 이름만 콤보박스에 넣는다면...
    // 나머지 정보를 구조체등에 넣은다음.. 그 포인터를 SetItemData()로 넣어두면
    // 언제든지 쉽게 접근할 수 있게된다.

    s.Format("0x%X", (UINT)m_combo.GetItemData(nIndex));
    SetDlgItemText(IDC_STATIC_OPTION, s);
}

선택되어진 아이템의 부가정보를 읽어와 숫자로 읽어온다음 문자열로 변환하여
해당 스태틱 컨트롤에 뿌려준다.

void CSssDlg::OnButton5() 
{
    int count = m_combo.GetCount(), i;
    for(i=0; i<count; i++)
        m_combo.SetItemData(i, (DWORD)rand());
}
콤보박스의 갯수를 세어, 부가정보로 랜덤한 숫자를 임시로 넣어본다.

int CComboBox::SetItemData(int nIndex, DWORD_PTR dwItemData);
------------------------------------------------------------------
Parameter
   nIndex - 제로베이스 인덱스
   dwITemData - 아이템에 넣을 부가정보의 포인터, 데이터를 넣을 경우는 DWORD로 타입 캐스팅
       하여 넣으면 된다. 
Return Value
   현재 아이템의 제로베이스 인덱스
Remark
   만약 new 등을 이용해 할당한 것이라면 수동으로 delete 해주어야 한다.


6. 다음 그림처럼 버튼을 넣고, 그 버튼을 누르면 선택된 아이템의 캡션을 읽어온다.

void CSssDlg::OnButton6() 
{
    int nIndex = m_combo.GetCurSel();
    if(nIndex == -1)
        return;

    CString s;
    m_combo.GetLBText(nIndex, s);
    AfxMessageBox(s);
}

선택되어진 아이템의 인덱스를 구한 후, 아이템의 문자열을 읽어와 메시지박스로 뿌려준다.

void CComboBox::GetLBText(int nIndex, CString& rString) const;
----------------------------------------------------------------
Parameter
   nIndex - 제로베이스 인덱스
   rString - 읽어올 문자열을 저장할 객체.


그 외에도 콤보박스에서 제공되는 함수나 기능들은 좀더 다양하게 있지만 그렇게
자주 쓰이거나 하지는 않는다. 

다음 장에서 콤보박스의 이벤트를 다루며 몇몇 다른 함수들의 기능도 살펴보자.




 

반응형
반응형

view클래스::OnInitialUpdate()
{

 
 CComboBox * pWnd=(CComboBox*)(GetDlgItem(IDC_COMBO1));
  //->InsertString((CComboBox*)(::GetDlgItem(m_hWnd,IDC_COMBO1))->GetCount(), str);

 
 CString str;
 str.Format(L"첫번째아이템");
 pWnd->InsertString(pWnd->GetCount(), str);

 

 

...

}


반응형
반응형

다른것은 동일한데 새로운 사용자 정의 클래스에서

 

doc 클래스와 view 클래스를 include 한다 이때 순서가 중요한데

 

doc클래스 다음에 view 클래스를 include 해야한다

 

 

하지만 뷰클래스에 위에 설명한 클래스를 포함하려면 에러가 나는데

이럴거면 차라리 뷰클래스 아래 클래스를 작성하고 뷰클래스를 그대로 상속 받기만 해도 상속이 되며

 

사용자 정의 클래스를 뷰 클래스 상단에 선언문만 작성해 놓고 쓰면 에러를 피할 수 있다

반응형

+ Recent posts